{
	"version": "https://jsonfeed.org/version/1",
	"title": "SonicBlog",
	"icon": "https://blog.sonicbunny.com/uploads/2024/favicon-medium.png",
	"home_page_url": "https://blog.sonicbunny.com/",
	"feed_url": "https://blog.sonicbunny.com/feed.json",
	"items": [
			{
				"id": "http://sonicbunny-blog.micro.blog/2024/06/28/loupedeck-lights-with.html",
				"title": "Loupedeck Lights with GlowWorm and Shortcuts",
				"content_html": "<p>With <a href=\"https://blog.sonicbunny.com/2024/06/28/new-release-glowworm.html\">GlowWorm 1.3</a>, we&rsquo;re excited to add support for Apple Shortcuts!  One of the cool things this enables is integration with physical control decks like the <a href=\"https://amzn.to/4cr4B7C\">Loupedeck</a> and <a href=\"https://amzn.to/4cr4B7C\">Stream Deck</a>.  In the SonicBunny Labs, we&rsquo;ve got a <a href=\"https://amzn.to/4cr4B7C\">Loupedeck</a> for testing, so let&rsquo;s see how to set up a simple button to activate a scene.</p>\n<p>Create a new Apple Shortcut.  We&rsquo;ll call ours &lsquo;Turn on Waves&rsquo; because we want to activate a scene called &lsquo;Waves&rsquo;.  (That turns on a rainbow wave on the Labs' <a href=\"https://amzn.to/4aH7q3c\">Litra Beam LX</a>.)  Search for the GlowWorm app in the library sidebar on the right and drag the &lsquo;Activate Scene&rsquo; action into the shortcut.  Click the &lsquo;Scene&rsquo; placeholder and select the scene you&rsquo;d like to activate.  And we&rsquo;re done with Step 1!  You can hit the run button to test the scene.</p>\n<img style=\"display:block; margin-left:auto; margin-right:auto;\" src=\"https://blog.sonicbunny.com/uploads/2024/glowworm-shortcuts-scene.png\" alt=\"The editor interface in Apple Shortcuts showing a simple scene with only one action -- to activate a GlowWorm scene called Waves.\" title=\"GlowWorm_Shortcuts_Scene.png\" border=\"0\" width=\"600\" height=\"343\" />\n<p>In the LoupedeckConfig tool, you&rsquo;re going to add a new Custom entry.  From the &lsquo;Custom&rsquo; list, select &lsquo;Run Command&rsquo;.  In the &lsquo;Paste path here&rsquo; box, you&rsquo;re going to add the following command:</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#1f1f24;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-fallback\" data-lang=\"fallback\"><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\">1</span><span style=\"color:#fff\">/usr/bin/shortcuts || run &#34;Turn on Waves&#34; \n</span></code></pre></div><p>That runs the command line tool <code>shortcuts</code> with the arguments <code>run &quot;Turn on Waves&quot;</code>.  Loupedeck uses the <code>||</code> to separate the command and the arguments, so don&rsquo;t leave those out.  Remember, &lsquo;Turn on Waves&rsquo; is the name of our shortcut in the Shortcuts app, so if you named yours differently, be sure to adjust.  You can also run the command <code>/usr/bin/shortcuts list</code> in the terminal to get a list of the name of all your shortcuts.</p>\n<p>To finish setting up your new &lsquo;Run Command Action&rsquo;, pick a name and set the icon.  Once you save all that, you can drag it onto a Loupedeck button and you&rsquo;re ready to go.</p>\n<img style=\"display:block; margin-left:auto; margin-right:auto;\" src=\"https://blog.sonicbunny.com/uploads/2024/loupedeck-glowworm-scene-action.png\" alt=\"Loupedeck GlowWorm Scene Action.\" title=\"Loupedeck_GlowWorm_Scene_Action.png\" border=\"0\" width=\"600\" height=\"266\" />\n<p>In just two simple steps, we can build a shortcut and assign it to a Loupedeck button.  Now any GlowWorm scene is ready at the touch of a button.  You can use that to bring up your standard lights as Zoom joins a meeting or you can cycle between scenes to highlight extra drama on your Twitch stream.</p>\n<p>Let us know if you&rsquo;ve got feedback or suggestions for better integration between GlowWorm and Apple Shortcuts!</p>\n",
				"date_published": "2024-06-28T11:05:19-05:00",
				"url": "https://blog.sonicbunny.com/2024/06/28/loupedeck-lights-with.html",
				"tags": ["GlowWorm"]
			},
			{
				"id": "http://sonicbunny-blog.micro.blog/2024/06/28/new-release-glowworm.html",
				"title": "New Release — GlowWorm 1.3!",
				"content_html": "<img style=\"display:block; margin-left:auto; margin-right:auto;\" src=\"https://blog.sonicbunny.com/uploads/2024/glowworm-logo.png\" alt=\"Glowworm logo\" title=\"glowworm logo.png\" border=\"0\" width=\"600\" height=\"285\" />\n<p>We’re excited to announce GlowWorm 1.3 — the “Take a Shortcut” release!</p>\n<p>GlowWorm is now fully Shortcuts enabled!  Once GlowWorm 1.3 is installed, you&rsquo;ll see a new suite of GlowWorm actions in your Apple Shortcuts library.  From a shortcut, you can activate a scene or control an individual light.  Now you can make a &lsquo;video call&rsquo; workflow that pauses your music, activates your &lsquo;Professional&rsquo; GlowWorm scene and starts up Zoom.</p>\n<p>GlowWorm actions in Shortcuts are fantastic for integrating physical control decks like the Loupedeck or a Stream Deck.  Via Shortcuts, you can tie an action to a button and run that action in the background with quick tap.  Now you can use GlowWorm to switch scenes on the fly in the middle of your live stream!</p>\n<p>GlowWorm actions also let you get a list of your available scenes and lights for even more intricate Shortcuts.  Turn on a random light instead of flipping a coin.  Make a shortcut to cycle through all your scenes to see which one makes that new sweater really pop!  We&rsquo;re so excited to hear what wild things you come up with.</p>\n<p>Enjoy and let us know what you think!</p>\n<center>\n<a href=\"https://apps.apple.com/us/app/glowworm-lighting/id6446908196?mt=12\">\n<div style=\"vertical-align: middle\">\n<div style=\"display: flex; align-items: center;\">\n<img src=\"https://blog.sonicbunny.com/uploads/2024/glowworm-favicon-small.png\" alt=\"GlowWorm favicon small.\" title=\"GlowWorm\" border=\"0\" style=\"height: 137px; width: auto; display: inline-block; vertical-align: middle;\" />\n<p style=\"padding: 25px;\">\nGet it on the Mac App Store!\n</p>\n<img  src=\"https://blog.sonicbunny.com/uploads/2024/stacks-image-73685f1.png\" alt=\"Download on the Mac App Store\" title=\"stacks-image-73685f1.png\" border=\"0\" style=\"height: 80px; width: auto;\"/>\u2028</div>\n</div>\n</a>\n</center>\n",
				"date_published": "2024-06-28T10:30:39-05:00",
				"url": "https://blog.sonicbunny.com/2024/06/28/new-release-glowworm.html",
				"tags": ["GlowWorm"]
			},
			{
				"id": "http://sonicbunny-blog.micro.blog/2024/06/04/new-release-glowworm.html",
				"title": "New Release — GlowWorm 1.2!",
				"content_html": "<img style=\"display:block; margin-left:auto; margin-right:auto;\" src=\"https://blog.sonicbunny.com/uploads/2024/glowworm-logo.png\" alt=\"Glowworm logo\" title=\"glowworm logo.png\" border=\"0\" width=\"600\" height=\"285\" />\n<p>We’re excited to announce GlowWorm 1.2 — the “Make a Scene” release!</p>\n<p>A GlowWorm scene saves all the settings for your lights and lets you restore settings those with a click.  Now you can make a scene for work meetings in Zoom that’s very professional and polished but create another scene for Twitch streaming with rainbow colors and wave effects.</p>\n<p>We’ve made it really easy to switch scenes too.  Of course you can switch from the main GlowWorm interface, but you can also right click on the dock icon to get a list of scenes or enable the new menu bar helper so your GlowWorm scene is only a few clicks away.</p>\n<p>With scenes available, we’ve started adding support for automations.  Each scene has a URL that you can use to activate it from another app or workflow.  For instance, you can assign a scene URL to a Loupedeck button or use it in a script.  This is just the beginning for automation, so stay tuned!</p>\n<p>The new version also has a few ‘nice to have’ features like the camera preview.  You can switch on the camera preview and see how you look with the lighting setup.  Using that, you can tweak the settings so they’re just right when it comes time to be on camera for real.  We’ve also got a new setting to sync your lights to your Mac’s display so when the Mac goes to sleep, so do the lights.  When the Mac wakes up, they’ll pop right back on too.  Great for us forgetful types that wander off with the all the gear on.</p>\n<p>Enjoy and let us know what you think!</p>\n<center>\n<a href=\"https://apps.apple.com/us/app/glowworm-lighting/id6446908196?mt=12\">\n<div style=\"vertical-align: middle\">\n<div style=\"display: flex; align-items: center;\">\n<img src=\"https://blog.sonicbunny.com/uploads/2024/glowworm-favicon-small.png\" alt=\"GlowWorm favicon small.\" title=\"GlowWorm\" border=\"0\" style=\"height: 137px; width: auto; display: inline-block; vertical-align: middle;\" />\n<p style=\"padding: 25px;\">\nGet it on the Mac App Store!\n</p>\n<img  src=\"https://blog.sonicbunny.com/uploads/2024/stacks-image-73685f1.png\" alt=\"Download on the Mac App Store\" title=\"stacks-image-73685f1.png\" border=\"0\" style=\"height: 80px; width: auto;\"/>\u2028</div>\n</div>\n</a>\n</center>\n",
				"date_published": "2024-06-04T10:42:00-05:00",
				"url": "https://blog.sonicbunny.com/2024/06/04/new-release-glowworm.html",
				"tags": ["GlowWorm"]
			},
			{
				"id": "http://sonicbunny-blog.micro.blog/2024/05/01/172412.html",
				"title": "SwiftUI Testing -- Part the Second",
				"content_html": "<p>We&rsquo;re continuing our look at unit testing with SwiftUI.  In <a href=\"https://blog.sonicbunny.com/2024/05/01/swiftui-testing-part.html\">Part One</a>, we looked back at how we tested UIKit and what approach makes sense to validate our SwiftUI projects.</p>\n<p>For unit testing, test coverage is an important metric.  How much of your code base are you exercising during unit tests?  Xcode is, unfortunately, not too bright when it comes to measuring coverage.  It doesn&rsquo;t have the intelligence to know if you&rsquo;re &ldquo;testing&rdquo; a line of code, just that the line of code was executed.  This is a problem right off the bat.</p>\n<p>I&rsquo;ve got a new app project in Xcode.  The app doesn&rsquo;t do much yet and I want to start adding tests before it gets too big.  So I add a unit test target and Xcode plops in some empty tests.  I run the unit tests, and they all pass since they&rsquo;re empty.  I check the coverage and it&rsquo;s at 36%!  How can that be, I didn&rsquo;t test <em>anything</em>?</p>\n<p>Xcode, the blissful idiot, is really reporting that 36% of the code was executed while running the unit tests.  Usually, when an app starts up, it does some bootstrapping.  You might set up your persistent storage, draw a couple of Views on screen, and maybe talk to the network.  Xcode counts all that as &ldquo;testing&rdquo; because it ran during a unit test.</p>\n<p>To make the test coverage more accurate, we need to do as little as possible outside of our actual unit tests.  Jon Reid <a href=\"https://qualitycoding.org/ios-app-delegate-testing/\">has a write-up</a> of how to do this with a UIKit app by swapping out the app delegate during tests.  But SwiftUI introduces a whole new startup sequence so we need a new approach for SwiftUI&rsquo;s new app lifecycle.</p>\n<p>After some futzing around, turns out it&rsquo;s easy!</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#1f1f24;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\"> 1</span><span style=\"color:#fc5fa3\">struct</span><span style=\"color:#fff\"> </span><span style=\"color:#5dd8ff\">TestApp</span><span style=\"color:#fff\">:</span><span style=\"color:#fff\"> </span><span style=\"color:#fff\">App</span><span style=\"color:#fff\"> </span><span style=\"color:#fff\">{</span><span style=\"color:#fff\">\t\t\t\t\t</span><span style=\"color:#6c7986\">// 1</span><span style=\"color:#fff\">\n</span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\"> 2</span><span style=\"color:#fff\">    </span><span style=\"color:#fc5fa3\">var</span><span style=\"color:#fff\"> </span><span style=\"color:#41a1c0\">body</span><span style=\"color:#fff\">:</span><span style=\"color:#fff\"> </span><span style=\"color:#fff\">some</span><span style=\"color:#fff\"> </span><span style=\"color:#fff\">Scene</span><span style=\"color:#fff\"> </span><span style=\"color:#fff\">{</span><span style=\"color:#fff\">\n</span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\"> 3</span><span style=\"color:#fff\">        </span><span style=\"color:#fff\">WindowGroup</span><span style=\"color:#fff\"> </span><span style=\"color:#fff\">{</span><span style=\"color:#fff\">\n</span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\"> 4</span><span style=\"color:#fff\">            </span><span style=\"color:#fff\">Text</span><span style=\"color:#fff\">(</span><span style=\"color:#fc6a5d\">&#34;I&#39;m running tests!&#34;</span><span style=\"color:#fff\">)</span><span style=\"color:#fff\">\n</span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\"> 5</span><span style=\"color:#fff\">        </span><span style=\"color:#fff\">}</span><span style=\"color:#fff\">\n</span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\"> 6</span><span style=\"color:#fff\">    </span><span style=\"color:#fff\">}</span><span style=\"color:#fff\">\n</span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\"> 7</span><span style=\"color:#fff\"></span><span style=\"color:#fff\">}</span><span style=\"color:#fff\">\n</span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\"> 8</span><span style=\"color:#fff\">\n</span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\"> 9</span><span style=\"color:#fff\"></span><span style=\"color:#fff\">@</span><span style=\"color:#fff\">main</span><span style=\"color:#fff\">\t\t\t\t\t\t\t\t\t\t</span><span style=\"color:#6c7986\">// 2</span><span style=\"color:#fff\">\n</span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\">10</span><span style=\"color:#fff\"></span><span style=\"color:#fc5fa3\">struct</span><span style=\"color:#fff\"> </span><span style=\"color:#5dd8ff\">TestDriver</span><span style=\"color:#fff\"> </span><span style=\"color:#fff\">{</span><span style=\"color:#fff\">\n</span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\">11</span><span style=\"color:#fff\">    </span><span style=\"color:#fc5fa3\">static</span><span style=\"color:#fff\"> </span><span style=\"color:#fc5fa3\">func</span><span style=\"color:#fff\"> </span><span style=\"color:#41a1c0\">main</span><span style=\"color:#fff\">()</span><span style=\"color:#fff\"> </span><span style=\"color:#fff\">{</span><span style=\"color:#fff\">\n</span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\">12</span><span style=\"color:#fff\">        </span><span style=\"color:#fc5fa3\">if</span><span style=\"color:#fff\"> </span><span style=\"color:#fff\">NSClassFromString</span><span style=\"color:#fff\">(</span><span style=\"color:#fc6a5d\">&#34;XCTestCase&#34;</span><span style=\"color:#fff\">)</span><span style=\"color:#fff\"> </span><span style=\"color:#fff\">!=</span><span style=\"color:#fff\"> </span><span style=\"color:#fc5fa3\">nil</span><span style=\"color:#fff\"> </span><span style=\"color:#fff\">{</span><span style=\"color:#fff\">    </span><span style=\"color:#6c7986\">// 3</span><span style=\"color:#fff\">\n</span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\">13</span><span style=\"color:#fff\">            </span><span style=\"color:#fff\">TestApp</span><span style=\"color:#fff\">.</span><span style=\"color:#fff\">main</span><span style=\"color:#fff\">()</span><span style=\"color:#fff\">\n</span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\">14</span><span style=\"color:#fff\">        </span><span style=\"color:#fff\">}</span><span style=\"color:#fff\"> </span><span style=\"color:#fc5fa3\">else</span><span style=\"color:#fff\"> </span><span style=\"color:#fff\">{</span><span style=\"color:#fff\">\n</span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\">15</span><span style=\"color:#fff\">            </span><span style=\"color:#fff\">MyRealApp</span><span style=\"color:#fff\">.</span><span style=\"color:#fff\">main</span><span style=\"color:#fff\">()</span><span style=\"color:#fff\">\n</span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\">16</span><span style=\"color:#fff\">        </span><span style=\"color:#fff\">}</span><span style=\"color:#fff\">\n</span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\">17</span><span style=\"color:#fff\">    </span><span style=\"color:#fff\">}</span><span style=\"color:#fff\">\n</span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\">18</span><span style=\"color:#fff\"></span><span style=\"color:#fff\">}</span><span style=\"color:#fff\">\n</span></code></pre></div><p>There are three key points that make this work:</p>\n<ol>\n<li>We need a dummy `App` struct to use instead of the real app.  This simple stand-in circumvents all your usual app startup machinery.  Instead of all the normal bootstrapping, we'll just get a window with the text \"I'm running tests!\".</li>\n<li>Remove the `@main` from your `App` implementation and add it here to `TestDriver`.  Swift uses `@main` to figure out how to start your app.  The `App` protocol provides a default implementation that, according to the docs, 'manages the launch process in a platform-appropriate way'.  But by inserting our own wrapper layer here around, we can control _which_ `main()` is called.</li>\n<li>That brings us to the final point, use the good old `NSClassFromString` to decide if the testing bundle has been injected into our process.  `XCTestCase` is only available during testing, so this is a reliable way to decide if unit testing is underway.  Based on that, we can call the `main` method of either our real app or our testing stand-in.  It turns out that the default implementation of `main` knows to use its parent struct to bootstrap the SwiftUI app.</li>\n</ol>\n<p>Now when I run unit tests, my coverage is at 0.8%!  That&rsquo;s more like it.  In order to boost my test coverage, I now have to actually test code.  And the code coverage metric really starts to mean something.</p>\n<p>Follow us on <a href=\"https://social.sonicbunny.com/@sonicbunny\">Mastodon</a> and <a href=\"https://www.linkedin.com/company/sonicbunny-software\">LinkedIn</a> for more info Swift, SwiftUI and software testability.  And if you&rsquo;re interested in help with testing your existing app or building a whole new app, don&rsquo;t hesitate to <a href=\"https://www.sonicbunny.com/Contact/\">get in touch</a>.  We&rsquo;d love to hear from you.</p>\n",
				"date_published": "2024-05-01T16:24:12-05:00",
				"url": "https://blog.sonicbunny.com/2024/05/01/172412.html",
				"tags": ["Swift","SwiftUI","Testing"]
			},
			{
				"id": "http://sonicbunny-blog.micro.blog/2024/05/01/new-release-glowworm.html",
				"title": "New Release — GlowWorm 1.1!",
				"content_html": "<img style=\"display:block; margin-left:auto; margin-right:auto;\" src=\"https://blog.sonicbunny.com/uploads/2024/glowworm-logo.png\" alt=\"Glowworm logo\" title=\"glowworm logo.png\" border=\"0\" width=\"600\" height=\"285\" />\n<p>SonicBunny Software is thrilled to announce the first update of our new app &ndash; GlowWorm 1.1!</p>\n<p>GlowWorm for your Mac allows you to control your <a href=\"https://www.logitechg.com/en-us/products/cameras-lighting.html\">Logitech Litra</a> streaming key lights.  GlowWorm is a light weight, fully native Mac utility that makes it quick and easy to configure your lights just the way you need.</p>\n<p>The 1.1 update supports all available lights in the Litra family, including:</p>\n<ul>\n<li><a href=\"https://www.logitech.com/en-us/products/lighting/litra-glow.946-000001.html\">Logitech Glow</a></li>\n<li><a href=\"https://www.logitechg.com/en-us/products/cameras-lighting/litra-beam-streaming-light.946-000019.html\">Logitech Beam</a></li>\n<li><a href=\"https://www.logitechg.com/en-us/products/cameras-lighting/litra-beam-lx-led-light.946-000013.html\">Logitech Beam LX light</a></li>\n</ul>\n<p>New features coming soon include:</p>\n<ul>\n<li><a href=\"https://www.elgato.com/us/en/p/key-light\">Elgato key lights</a></li>\n<li>Scenes</li>\n<li>Apple Shortcuts and Siri</li>\n</ul>\n<p>We&rsquo;re very excited to make GlowWorm a fast and flexible tool to control your streaming lighting.</p>\n<center>\n<a href=\"https://apps.apple.com/us/app/glowworm-lighting/id6446908196?mt=12\">\n<div style=\"vertical-align: middle\">\n<div style=\"display: flex; align-items: center;\">\n<img src=\"https://blog.sonicbunny.com/uploads/2024/glowworm-favicon-small.png\" alt=\"GlowWorm favicon small.\" title=\"GlowWorm\" border=\"0\" style=\"height: 137px; width: auto; display: inline-block; vertical-align: middle;\" />\n<p style=\"padding: 25px;\">\nGet it on the Mac App Store!\n</p>\n<img  src=\"https://blog.sonicbunny.com/uploads/2024/stacks-image-73685f1.png\" alt=\"Download on the Mac App Store\" title=\"stacks-image-73685f1.png\" border=\"0\" style=\"height: 80px; width: auto;\"/>\u2028</div>\n</div>\n</a>\n</center>\n",
				"date_published": "2024-05-01T16:22:25-05:00",
				"url": "https://blog.sonicbunny.com/2024/05/01/new-release-glowworm.html",
				"tags": ["GlowWorm"]
			},
			{
				"id": "http://sonicbunny-blog.micro.blog/2024/05/01/swiftui-testing-part.html",
				"title": "SwiftUI Testing -- Part the First",
				"content_html": "<p>Apple released SwiftUI (<a href=\"https://en.wikipedia.org/wiki/SwiftUI\">checks notes</a>) in 2019.  Wait what?  It&rsquo;s been 5 years?!  Wow.  In the beginning, the framework was a bit&hellip; sparse, but Apple has refined and improved it so now SwiftUI is worth considering for app development.  But an important part of any framework we want to include in our apps is testability.  How testable is SwiftUI?  What are some of the hurdles we need to overcome in order to quickly and accurately test our SwiftUI interface and make testing an integral part of our development workflow?</p>\n<p>SwiftUI aims to replace UIKit on iOS, so that&rsquo;s the benchmark in terms of testing.  Ideally, SwiftUI should be at least as good as UIKit in this respect.  The key to testing with UIKit is getting a view controller to execute its lifecycle.  In a blog post lost to the inevitable bit rot of the Internet, <a href=\"https://www.natashatherobot.com\">NatashaTheRobot</a> showed that if you instantiate a view controller and access it&rsquo;s <code>view</code> attribute, the system&rsquo;s runtime would cause the whole lifecycle to execute.  Using a Storyboard, the code for that looks something like this:</p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#1f1f24;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-swift\" data-lang=\"swift\"><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\">1</span><span style=\"color:#fc5fa3\">let</span><span style=\"color:#fff\"> </span><span style=\"color:#41a1c0\">storyBoard</span><span style=\"color:#fff\"> </span><span style=\"color:#fff\">=</span><span style=\"color:#fff\"> </span><span style=\"color:#fff\">UIStoryboard</span><span style=\"color:#fff\">(</span><span style=\"color:#fff\">name</span><span style=\"color:#fff\">:</span><span style=\"color:#fff\"> </span><span style=\"color:#fc6a5d\">&#34;MyStoryboard&#34;</span><span style=\"color:#fff\">,</span><span style=\"color:#fff\"> </span><span style=\"color:#fff\">bundle</span><span style=\"color:#fff\">:</span><span style=\"color:#fff\"> </span><span style=\"color:#fc5fa3\">nil</span><span style=\"color:#fff\">)</span><span style=\"color:#fff\">\n</span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\">2</span><span style=\"color:#fff\"></span><span style=\"color:#fc5fa3\">let</span><span style=\"color:#fff\"> </span><span style=\"color:#41a1c0\">viewController</span><span style=\"color:#fff\"> </span><span style=\"color:#fff\">=</span><span style=\"color:#fff\"> </span><span style=\"color:#fff\">sBoard</span><span style=\"color:#fff\">.</span><span style=\"color:#fff\">instantiateInitialViewController</span><span style=\"color:#fff\">()</span><span style=\"color:#fff\"> </span><span style=\"color:#fc5fa3\">as</span><span style=\"color:#fff\">?</span><span style=\"color:#fff\"> </span><span style=\"color:#fff\">MyViewController</span><span style=\"color:#fff\">\n</span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\">3</span><span style=\"color:#fff\"></span><span style=\"color:#fff\">XCTAssertNotNil</span><span style=\"color:#fff\">(</span><span style=\"color:#fff\">viewController</span><span style=\"color:#fff\">.</span><span style=\"color:#fff\">view</span><span style=\"color:#fff\">)</span><span style=\"color:#fff\">\n</span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8f8f91\">4</span><span style=\"color:#fff\">\n</span></code></pre></div><p>By accessing <code>view</code>, we trigger a whole slew of Apple magic to bootstrap the viewController&rsquo;s lifecycle including things like <code>viewDidLoad()</code>, <code>viewDidAppear()</code> and deserializing any components from the storyboard.  After that, we&rsquo;re free to poke and prod the VC and thoroughly unit test everything.  UIKit has really set the bar pretty high in terms of testability.  But trail blazers in the dev community have had many years to work all this out.  After five years of SwiftUI, how does it stack up?</p>\n<p>Well, that&rsquo;s where it gets tricky.  With UIKit, we&rsquo;re able to actually create the view controller in a test and interact with it directly.  In SwiftUI, our <code>View</code> structs aren&rsquo;t the actual interface, but a <em>description</em> of the interface.  The SwiftUI framework takes that description and creates real interfaces with UIKit on iOS or AppKit on the Mac.  So how do we test that?  We need help.  Ideally, Apple would provide that, but they don&rsquo;t, so we need to look elsewhere.  After five years, <a href=\"https://github.com/nalexn/ViewInspector\">ViewInspector</a> has become the de facto standard for filling that role.  Honestly, I&rsquo;m not sure the voodoo that&rsquo;s involved with ViewInspector, but tl;dr, it lets us create a view, inspect the view hierarchy and interact with controls.  Given that this is a third party, open source, community effort, ViewInspector is fantastic!  While they cover <em>most</em> of the SwiftUI API, you can see on the <a href=\"https://github.com/nalexn/ViewInspector/blob/0.10.0/readiness.md\">readiness list</a>, that some corners of the framework aren&rsquo;t supported.  And because our <code>View</code> structs are just descriptions of the UI and not the actual UI, I&rsquo;ve found it quite fragile to work with anything but the most basic code in the non-UI parts of the <code>View</code> struct.  But a <code>View</code> can often need to be quite complicated, so how do we deal with that?  One word &ndash; <a href=\"https://youtu.be/PSxihhBzCjk?si=bzouO3DtgvEj4Qbo&amp;t=29\">plastics!</a> &ndash; I mean ViewModel!  Or is that two words?</p>\n<p>Using a ViewModel, we talk all the logic (and I mean <em>all</em> the logic) out of the <code>View</code> and put it into a class we&rsquo;ll call <code>ViewModel</code>.  Even a simple <code>if</code> statement in the <code>View</code> moves to a computed property of the <code>ViewModel</code>.  We&rsquo;ll store an instance of <code>ViewModel</code> on the <code>View</code> and mark it as a <a href=\"https://developer.apple.com/documentation/swiftui/state\"><code>@State</code></a> variable.  Testability of this configuration turns out to be quite high.  In testing the <code>View</code> itself, we can easily build a fake subclass of the <code>ViewModel</code> and check that the view responds correctly to different states in the view model and calls the right methods with we interact with it via <code>ViewInspector</code>.  The <code>ViewModel</code> on the other hand, is just a class with no SwiftUI magic at all, so we can test that in the traditional ways with fakes, mocks, etc.</p>\n<p>Problem solved!  Almost.  While this works great in theory, in practice there are some potholes.  Most of the problems we encounter are related to incomplete coverage of the SwiftUI API by <code>ViewInspector</code>.  Again, the folks working on <code>ViewInspector</code> have done an enormous service to the community with all their hard work.  But they are chasing a moving target, with new API released by Apple every year, some of which is difficult or even impossible for a third party tool to work with.  But in the end, while this solution is not 100%, it&rsquo;s robust enough to provide a comfortably high degree of testing.  We&rsquo;ve been using it successfully with <a href=\"https://www.sonicbunny.com/GlowWorm/\">GlowWorm</a> for a while now.</p>\n<p>Follow us on <a href=\"https://social.sonicbunny.com/@sonicbunny\">Mastodon</a> and <a href=\"https://www.linkedin.com/company/sonicbunny-software\">LinkedIn</a> for more info Swift, SwiftUI and software testability.  And if you&rsquo;re interested in help with testing your existing app or building a whole new app, don&rsquo;t hesitate to <a href=\"https://www.sonicbunny.com/Contact/\">get in touch</a>.  We&rsquo;d love to hear from you.</p>\n",
				"date_published": "2024-05-01T15:46:43-05:00",
				"url": "https://blog.sonicbunny.com/2024/05/01/swiftui-testing-part.html",
				"tags": ["Swift","SwiftUI","Testing"]
			},
			{
				"id": "http://sonicbunny-blog.micro.blog/2024/04/04/if-youre-not.html",
				"title": "Vision Pro Personas.... In Space!!!",
				"content_html": "<p>If you&rsquo;re not keeping up with the play-by-play on the Apple Vision Pro (AVP), when you make a FaceTime call, the AVP represents you with an avatar thing called a Persona.  The Persona is a three dimensional, virtual version of you based on a scan of your actual face.  Your Persona changes to reflect the movements of your face, so people on your FaceTime call see the avatar smiling, frowning or (like me) looking vaguely confused.  When AVP first launched, <a href=\"https://www.macrumors.com/2024/01/31/apple-vision-pro-personas-deeply-weird/\">lots of reviews</a> complained that Personas came straight out of the uncanny valley.  With the VisionOS 1.1 release, Apple thankfully <a href=\"https://www.techradar.com/computing/virtual-reality-augmented-reality/apple-vision-pro-update-makes-personas-less-creepy-and-can-take-the-creation-process-out-of-your-hands\">turned down the creep factor</a>.</p>\n<p>The big news last week is that Apple released <a href=\"https://www.macrumors.com/2024/04/02/spatial-personas-beta-coming-to-vision-pro/\">Spatial Peronas</a> to Vision Pro users.  Now your Persona breaks out of its little phantom zone prison and inhabits the 3D space with other Personas on your call.  Jason Snell has a <a href=\"https://sixcolors.com/post/2024/04/spatial-persona-on-vision-pro-changes-the-game/\">nice writeup</a> of the first hand experience with the Spatial Personas and is worth a read.  The bottom line for me?  Spatial Personas plus SharePlay lay the foundation to make the AVP a fantastic tool for telepresence and collaboration!  That&rsquo;s in stark contrast to early <a href=\"https://www.theverge.com/23751675/apple-vision-pro-vr-headset-ios-17-mental-health-mood-journal\">worries that AVP would make us all lonely</a>.</p>\n<img style=\"display:block; margin-left:auto; margin-right:auto;\" src=\"https://blog.sonicbunny.com/uploads/2024/4e34d61b3245b95e7fbb8f88-3e0c3e37.jpeg\" alt=\"Obi-Wan, Luke and C3-PO watch as R2-D2 plays back a holigraphic recording of Princess Leia.\" title=\"4e34d61b3245b95e7fbb8f88_3e0c3e37.jpeg\" border=\"0\" width=\"599\" height=\"337\" />\n<center><i>Astro mech droids also support Spatial Personas.</i></center>\n<p>But the point I really want to highlight is Apple&rsquo;s long tradition of planning years (and years) ahead with their technology frameworks.  As <a href=\"https://chaos.social/@uliwitness/112213982673941988#.\">Uli pointed out</a>, they did this with the FrontRow software, which lead to the AppleTV hardware.  HealthKit was <a href=\"https://en.wikipedia.org/wiki/Health_(Apple)\">released in the Fall of 2014</a> along with <a href=\"https://en.wikipedia.org/wiki/Apple_Pay\">Apple Pay</a>, two tent pole technologies of <a href=\"https://en.wikipedia.org/wiki/Apple_Watch\">Apple Watch</a> which would be released the next year.  Side Car for macOS, <a href=\"https://en.wikipedia.org/wiki/MacOS_Catalina#Sidecar\">released in 2019</a>, paved the way for Mac Virtual Display on Vision Pro in 2024.  And of course one of their longest running projects is Apple Silicon.  In 2010, Apple shipped the A4 chip in the first generation iPad.  <a href=\"https://en.wikipedia.org/wiki/Apple_M1\">10 years later</a>,  Apple Silicon made the jump to Macs with the M1 and blew everyone away with unheard of performance and efficiency.</p>\n<p>So many of Apple&rsquo;s really stunning achievements turn out like a good murder mystery movie.  When you go back and watch the film again, you see clues along the way that hint at the ending.  Go back and look at the evolution of Apple&rsquo;s tech stack and you&rsquo;ll pieces of technology that eventually culminate in an amazing new feature.  At the time, I just assumed that SharePlay was a response to the pandemic to let isolated folks watch movies together.  That&rsquo;s cool, but it seemed like a really niche application.  But pair that with AVP and it completely transforms this very single user device to an immersive <em>and</em> connected experience that lets you collaborate with everyone.  I was a little lukewarm on the AVP initially, but Spatial Personas and SharePlay have opened up my line of thinking and&hellip; maybe it&rsquo;s fantastic!  Now we just need SharePlay for Xcode please.  Counting down to WWDC 24!</p>\n",
				"date_published": "2024-04-04T13:29:41-05:00",
				"url": "https://blog.sonicbunny.com/2024/04/04/if-youre-not.html",
				"tags": ["Apple","VisionPro"]
			},
			{
				"id": "http://sonicbunny-blog.micro.blog/2024/03/22/protip-making-text.html",
				"title": "ProTip -- Making Text Selections",
				"content_html": "<p>This tip is a simple one, but I&rsquo;m surprised to find people aren&rsquo;t aware of the many ways to select text on your Mac.  If you&rsquo;re working on your Mac, you&rsquo;re probably dealing with text.  That means making selections for formatting, copying, pasting, and so on.  This is basic stuff &ndash; click on the start of the text and drag to the end.  Everyone who can &lsquo;use a computer&rsquo; knows that, but on a Mac there are a few more ways to select.</p>\n<ul>\n<li>\n<p>Double click a word to select the whole word.  This usually goes from whitespace to whitespace around the word and its a great way quickly select a word when need to grab a name out of an email or a variable out of some code.</p>\n</li>\n<li>\n<p>Triple click to select a whole paragraph.  Say you&rsquo;re researching something online and come across the salient point on a web page.  Just triple click the paragraph and you zip that off your team.</p>\n</li>\n<li>\n<p>Shift click to select a range.  This one is good for selecting a lot of text that doesn&rsquo;t fit on the screen, like a long web page or a big Word doc.  Just click on the start point, scroll, scroll, scroll and shift click on the end point.  Voila!  The whole thing is selected.</p>\n</li>\n<li>\n<p>Extend the selection with shift click.  If you have some text selected and shift-click again, the selection will extend to the point you&rsquo;ve clicked.  Sometimes you might not get all the text in the first swipe, so by shift clicking, you can extend the selection and get those missed letters.</p>\n</li>\n</ul>\n<p>Mac features like these selection tricks are the sorts of things that sometimes fall through the cracks.  Long time Mac users have these actions embedded in muscle memory and can&rsquo;t really say where they learned them.  But they&rsquo;re not very discoverable, so new users sometimes miss out on these deeper features.  So spread the word!  Tell your friends!  Triple click all the things!</p>\n<img style=\"display:block; margin-left:auto; margin-right:auto;\" src=\"https://blog.sonicbunny.com/uploads/2024/triple-click.jpg\" alt=\"Dorothy&#39;s ruby slippers from the Wizard of Oz with the caption &#39;Triple Click All the Things!&#39;\" title=\"triple_click.jpg\" border=\"0\" width=\"577\" height=\"432\" />\n",
				"date_published": "2024-03-22T08:41:58-05:00",
				"url": "https://blog.sonicbunny.com/2024/03/22/protip-making-text.html",
				"tags": ["ProTip"]
			},
			{
				"id": "http://sonicbunny-blog.micro.blog/2024/03/13/the-magic-of.html",
				"title": "The Magic of Macintosh",
				"content_html": "<p>We got our first Mac in late 1999, around the time Jeff Goldblum was explaining that there <a href=\"https://www.youtube.com/watch?v=rjY0xsoozs8\">is no step three</a>.  Fedex brought us a purple <a href=\"https://en.wikipedia.org/wiki/IMac_G3#iMac_DV\">iMac DV</a>, a gumdrop of a computer from a future with no floppy disks.  That Mac was fantastic.  I&rsquo;ve lost count of the Macs that have come and gone since then, but they&rsquo;ve all been magical in their own way.  One thing I wanted to do with this blog is to explore some of the lesser known features of the Mac and show how they can make using a Mac even more efficient and delightful.</p>\n<center>\n<img src=\"https://blog.sonicbunny.com/uploads/2024/apple-imac-400.jpg\" alt=\"A purple iMac DV circa 1999\" title=\"apple_imac_400.jpg\" style = \"width: 170px;\" />\n</center>\n<p>There are a lot of names given to apps on the Mac that really embrace the system.  Some call them &lsquo;Mac good citizens&rsquo; and some to say they&rsquo;re <a href=\"https://daringfireball.net/linked/2020/03/20/mac-assed-mac-apps\">Mac-assed Mac apps</a>.  With SonicBunny&rsquo;s Mac apps, that&rsquo;s the goal &ndash; to make a Mac-assed Mac app that feels right at home.  And maybe just a little bit magical.</p>\n<p>So let&rsquo;s dig right in.  I&rsquo;ll start with a feature that I&rsquo;ve used a number of times writing this post and that&rsquo;s the word &lsquo;look up&rsquo; feature.  You can access this by right clicking on a word and choosing <a href=\"https://support.apple.com/en-gb/guide/mac-help/mchl3983326c/mac\">&lsquo;Look up&rsquo;</a> in the context menu.  If you&rsquo;re using a track pad, you can <a href=\"https://support.apple.com/en-us/102309\">force click</a> on a word to look it up.  For example, if I &lsquo;look up&rsquo; the word &lsquo;marvels&rsquo;, I get this pop up.</p>\n<img src=\"https://blog.sonicbunny.com/uploads/2024/d5f074a43a.png\" alt=\"Screen shot of the &#39;look up&#39; menu for the word &#39;marvels&#39;.\" title=\"UntitledImage.png\" border=\"0\" width=\"545\" height=\"600\" style=\"display:block; margin-left:auto; margin-right:auto;\"/>\n<p>You get immediate access to the dictionary definition of the word as well as synonyms and antonyms.  For me, this is incredibly useful in writing.  Sometimes it&rsquo;s helpful to double check the meaning of a word, but more commonly, I need help from the thesaurus when I&rsquo;ve used the word &lsquo;reliable&rsquo; three times in as many sentences.  macOS has supported &lsquo;look up&rsquo; for dictionary and thesaurus for a very long time, but in recent editions, Apple has added Siri Suggestions.  In the case of &lsquo;marvels&rsquo;, I get extra tabs along the bottom for TV shows, images, and websites.  Other search link to the Wikipedia and Apple Music.  &lsquo;Look up&rsquo; is also great reading the web in Safari.  You can quickly look up a word or phrase without leaving the current page.</p>\n<p>So that&rsquo;s our first <a href=\"https://blog.sonicbunny.com/categories/protip/\">Pro Tip</a> &ndash; use your track pad&rsquo;s &lsquo;force click&rsquo; to look up words quickly as you read and write on your Mac.</p>\n",
				"date_published": "2024-03-13T14:42:37-05:00",
				"url": "https://blog.sonicbunny.com/2024/03/13/the-magic-of.html",
				"tags": ["ProTip"]
			},
			{
				"id": "http://sonicbunny-blog.micro.blog/2024/03/11/new-release-glowworm.html",
				"title": "New Release — GlowWorm 1.0!",
				"content_html": "<img style=\"display:block; margin-left:auto; margin-right:auto;\" src=\"https://blog.sonicbunny.com/uploads/2024/glowworm-logo.png\" alt=\"Glowworm logo\" title=\"glowworm logo.png\" border=\"0\" width=\"600\" height=\"285\" />\n<p>SonicBunny Software is thrilled to announce the initial release of our new app &ndash; GlowWorm!</p>\n<p>GlowWorm for your Mac allows you to control your <a href=\"https://www.logitechg.com/en-us/products/cameras-lighting.html\">Logitech Litra</a> streaming key lights.  GlowWorm is a light weight, fully native Mac utility that makes it quick and easy to configure your lights just the way you need.</p>\n<p>Our initial release supports the <a href=\"https://www.logitech.com/en-us/products/lighting/litra-glow.946-000001.html\">Logitech Glow</a> and <a href=\"https://www.logitechg.com/en-us/products/cameras-lighting/litra-beam-streaming-light.946-000019.html\">Logitech Beam</a> lights.  New features coming soon include:</p>\n<ul>\n<li><a href=\"https://www.logitechg.com/en-us/products/cameras-lighting/litra-beam-lx-led-light.946-000013.html\">Logitech Beam LX light</a></li>\n<li><a href=\"https://www.elgato.com/us/en/p/key-light\">Elgato key lights</a></li>\n<li>Scenes</li>\n<li>Apple Shortcuts and Siri</li>\n</ul>\n<p>We&rsquo;re very excited to make GlowWorm a fast and flexible tool to control your streaming lighting.</p>\n<center>\n<a href=\"https://apps.apple.com/us/app/glowworm-lighting/id6446908196?mt=12\">\n<div style=\"vertical-align: middle\">\n<div style=\"display: flex; align-items: center;\">\n<img src=\"https://blog.sonicbunny.com/uploads/2024/glowworm-favicon-small.png\" alt=\"GlowWorm favicon small.\" title=\"GlowWorm\" border=\"0\" style=\"height: 137px; width: auto; display: inline-block; vertical-align: middle;\" />\n<p style=\"padding: 25px;\">\nGet it on the Mac App Store!\n</p>\n<img  src=\"https://blog.sonicbunny.com/uploads/2024/stacks-image-73685f1.png\" alt=\"Download on the Mac App Store\" title=\"stacks-image-73685f1.png\" border=\"0\" style=\"height: 80px; width: auto;\"/>\u2028</div>\n</div>\n</a>\n</center>\n",
				"date_published": "2024-03-11T13:58:59-05:00",
				"url": "https://blog.sonicbunny.com/2024/03/11/new-release-glowworm.html",
				"tags": ["GlowWorm"]
			}
	]
}
