<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
 <title>Virtual Sanity</title>
 <link href="https://www.virtualsanity.com/feed/" rel="self"/>
 <link href="https://www.virtualsanity.com/"/>
 <updated>2026-03-19T12:18:55-04:00</updated>
 <id>https://www.virtualsanity.com/</id>

 
 <entry>
   <title>Murder</title>
   <link href="https://www.virtualsanity.com/202601/murder/"/>
   <updated>2026-01-12T10:13:51-05:00</updated>
   <id>https://www.virtualsanity.com/202601/murder</id>
   <author>
      <name>John Brayton</name>
      <uri>https://www.virtualsanity.com/</uri>
   </author>
   <content type="html">&lt;p&gt;In July, a man died less than two miles from my home while being pinned to the ground by police officers. The police had been called because the man was acting erratically and having some sort of health event. I was deeply concerned, and I wanted a thorough investigation of the incident. If the police officers used excessive force or acted with ill intent, I want them to be held accountable.&lt;/p&gt;

&lt;p&gt;There were a number of protests in the area shortly after this incident. I decided not to attend them. I anticipated that some attendees of those protests would accuse the police officers of murder. Since the deceased seemed to be having a health event before police were called, I did not know whether the police were to blame. Therefore I did not want to participate in a protest that might make such accusations.&lt;/p&gt;

&lt;p&gt;I was also confident that there would be a thorough investigation. I hope that this confidence was well placed.&lt;/p&gt;

&lt;p&gt;The death of Renee Nicole Good is a very different situation. The administration has made it clear that they are avoiding investigation. The FBI is trying to block state and local officials from investigating. I hope that state and local officials are able to investigate and prosecute despite this, but I am skeptical.&lt;/p&gt;

&lt;p&gt;JD Vance &lt;a href=&quot;https://x.com/JDVance/status/2009688858036986310&quot;&gt;suggests&lt;/a&gt; that we &lt;a href=&quot;https://x.com/AlphaNews/status/2009679932289626385&quot;&gt;watch video of the incident&lt;/a&gt;, so I did. I also watched several breakdowns of the video footage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=ILyy5zT5pQ0&quot;&gt;Jen Psaki at MS NOW&lt;/a&gt;
&lt;li&gt;&lt;a href=&quot;https://www.thebulwark.com/p/breaking-new-footage-in-minneapolis&quot;&gt;Tim Miller at The Bulwark&lt;/a&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=F_7tjt_CdVg&quot;&gt;Jon Favreau and Tommy Vietor at Pod Save America&lt;/a&gt;
&lt;/ul&gt;

&lt;p&gt;Based on all that video footage, it looks like the car was moving slowly and away from the ICE thug. It also like looks the ICE thug walks away without a scratch from this slow-moving vehicle. It looks like he was in about as much danger as he would be walking through any grocery store parking lot. Based on the words uttered just after the shooting, it sounds like he shot her out of unwarranted anger &amp;mdash; not out of self-defense.&lt;/p&gt;

&lt;p&gt;This was a murder.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Moving from Linode to Hetzner</title>
   <link href="https://www.virtualsanity.com/202512/moving-from-linode-to-hetzner/"/>
   <updated>2025-12-08T09:53:42-05:00</updated>
   <id>https://www.virtualsanity.com/202512/moving-from-linode-to-hetzner</id>
   <author>
      <name>John Brayton</name>
      <uri>https://www.virtualsanity.com/</uri>
   </author>
   <content type="html">&lt;p&gt;Some of &lt;a href=&quot;https://www.goldenhillsoftware.com/unread/&quot;&gt;Unread&lt;/a&gt;&amp;rsquo;s functionality requires server infrastructure. I run servers to support both Unread Cloud and Unread&amp;rsquo;s webpage text functionality.&lt;/p&gt;

&lt;p&gt;Linode was my hosting provider of choice for a long time, but I recently moved these systems to Hetzner. I wanted to write a bit about how Linode and Hetzner compare.&lt;/p&gt;

&lt;h3&gt;Price and Performance&lt;/h3&gt;

&lt;p&gt;Hetzner is far more cost-effective for me. This is the pricing in US dollars for some of Linode&amp;rsquo;s low-end instances:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Nanode 1GB &amp;mdash; $5.00/month&lt;/b&gt;&lt;br&gt;
1GB RAM. 1 CPU.&lt;br&gt;
I was unable to get Geekbench scores for this because Geekbench requires 2GB of memory.
&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Linode 2GB &amp;mdash; $12.00/month&lt;/b&gt;&lt;br&gt;
2GB RAM. 1 CPU.&lt;br&gt;
Geekbench scores: 1323 single-core, 1317 multi-core.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Linode 4GB &amp;mdash; $24.00/month&lt;/b&gt;&lt;br&gt;
4GB RAM. 2 CPUs.&lt;br&gt;
Geekbench scores: 1095 single-core, 2036 multi-core.&lt;/p&gt;

&lt;p&gt;This is the pricing in US dollars for some of Hetzner&amp;rsquo;s low-end instances in Nuremberg, Falkenstein, and Helsinki:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;cx23 &amp;mdash; &lt;strike&gt;$3.49/month&lt;/strike&gt; $4.99/month effective April 1, 2026&lt;/b&gt;&lt;br&gt;
4GB RAM. 2 VCPUs.&lt;br&gt;
Geekbench scores: 769 single-core, 1377 multi-core.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;cpx22 &amp;mdash; &lt;strike&gt;$6.99/month&lt;/strike&gt; $9.49/month effective April 1, 2026&lt;/b&gt;&lt;br&gt;
4GB RAM. 2 VCPUs.&lt;br&gt;
Geekbench scores: 1902 single-core, 3484 multi-core.&lt;/p&gt;

&lt;p&gt;&lt;i&gt;These systems all have shared CPUs. Geekbench scores are likely less meaningful for shared CPU instances than they would be for dedicated CPU instances.&lt;/i&gt;&lt;/p&gt;

&lt;h3&gt;IPv4 Addresses&lt;/h3&gt;

&lt;p&gt;Each Hetzner instance includes an IPv6 address. Hetzner charges an additional $0.60/month for an instance to have a public IPv4 address. Linode includes an IPv4 address and an IPv6 address with each instance. This additional cost needs to be taken into account for a Hetzner instance that needs an IPv4 address.&lt;/p&gt;

&lt;p&gt;An instance needs a public IPv4 address if it accepts incoming requests from arbitrary clients, or if it makes outgoing requests to arbitrary servers. I was able to skip IPv4 addresses on instances that just run backend services such as Redis and PostgreSQL.&lt;/p&gt;

&lt;p&gt;I find instances without IPv4 addresses to be inconvenient in unexpected ways. For example, accessing github.com and accessing Wasabi&amp;rsquo;s S3-compatible storage both require IPv4. While I am able to connect to IPv6 IP addresses from my home/office network, I am sometimes unable to do so from public Wi-Fi networks.&lt;/p&gt;

&lt;h3&gt;Server Limits&lt;/h3&gt;

&lt;p&gt;In a Linode account, I was always able to create servers that I wanted.&lt;/p&gt;

&lt;p&gt;Hetzner limits customers to a specific number of servers. A customer can request increases. I was initially limited to 5 servers. That would have been insufficient to move most of my cloud infrastructure to Hetzner. After requesting an increase, I now have a limit of 30 servers.&lt;/p&gt;

&lt;p&gt;I have occasionally been unable to create a server of a specific type in a specific data center. I have worked around that by choosing a different data center.&lt;/p&gt;

&lt;h3&gt;Accessing Websites&lt;/h3&gt;

&lt;p&gt;Much of Unread&amp;rsquo;s server work consists of accessing feeds and webpages from websites. Hetzner&amp;rsquo;s instances at the pricing above are available in Nuremberg, Falkenstein, and Helsinki &amp;mdash; all of which are in the European Union. Some websites outside the EU that do not have international audiences block access to clients in the EU, presumably because those websites do not want to comply with EU privacy laws. Since Unread operates on behalf of customers inside and outside of the EU, it needs to be able to access websites and webpages from such websites.&lt;/p&gt;

&lt;p&gt;Hetzner does have data centers outside the European Union: specifically in Singapore; in Hillsboro, Oregon; and in Ashburn, Virginia. However, servers available in those data centers are more limited and more expensive.&lt;/p&gt;

&lt;p&gt;My solution for Unread&amp;rsquo;s servers was to host a proxy in a United States data center. When Unread&amp;rsquo;s feed retrieval system or webpage retrieval system get a forbidden response, they retry the request using the proxy server hosted in the United States.&lt;/p&gt;

&lt;h3&gt;Private Networks&lt;/h3&gt;

&lt;p&gt;Hetzner has the concept of a private network. A customer can create a 10.* network for a specific stack of servers. I found communicating across these private networks unreliable, and slower than communicating across the public IP addresses. &lt;a href=&quot;https://www.reddit.com/r/hetzner/comments/1mdxa44/do_you_use_private_networks_of_hetzner/&quot;&gt;Others&lt;/a&gt; seem to have similar experiences.&lt;/p&gt;

&lt;p&gt;On Linode I used the default 192.168.* private network. These networks were not completely private. They were accessible to other Linode customers in the same data center. Linode also has an ability to create &lt;a href=&quot;https://techdocs.akamai.com/cloud-computing/docs/create-a-private-network-with-vlans-using-the-api&quot;&gt;VLANs&lt;/a&gt; but I never explored it.&lt;/p&gt;

&lt;p&gt;In the end I ended up making my Hetzner instances communicate exclusively through public IP addresses. That private communication requires authentication and is encrypted. I would have preferred this communication to happen over a private network, but that was slower and less reliable.&lt;/p&gt;

&lt;h3&gt;Referral Links&lt;/h3&gt;

&lt;p&gt;I am happy with my decision to move to Hetzner. My servers are more powerful and less expensive. But Linode does have advantages over Hetzner.&lt;/p&gt;

&lt;p&gt;Referral links: &lt;a href=&quot;https://hetzner.cloud/?ref=CO5YycliXqet&quot;&gt;Hetzner&lt;/a&gt;, &lt;a href=&quot;https://www.linode.com/lp/refer/?r=965210170e7f76c093d1c10e7f863b5ef839b325&quot;&gt;Linode&lt;/a&gt;.&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;&lt;b&gt;Update February 26, 2026:&lt;/b&gt; Hetzner &lt;a href=&quot;https://www.hetzner.com/pressroom/statement-price-adjustment/&quot;&gt;recently announced&lt;/a&gt; a price increase effective April 1, 2026. I updated this post to reflect that price increase. I have not found any statement that the IPv4 prices are changing, so I assume that they are not.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Jimmy Kimmel Cancelled</title>
   <link href="https://www.virtualsanity.com/202509/jimmy-kimmel-cancelled/"/>
   <updated>2025-09-17T20:27:54-04:00</updated>
   <id>https://www.virtualsanity.com/202509/jimmy-kimmel-cancelled</id>
   <author>
      <name>John Brayton</name>
      <uri>https://www.virtualsanity.com/</uri>
   </author>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://daringfireball.net/2025/07/curse_not_the_king_cbs_colbert_trump&quot;&gt;John Gruber at Daring Fireball, when Colbert was cancelled&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
Even back in the now-comparatively-sane Trump 1.0 administration, it seemed palpably true to me that the best check against Trump&amp;rsquo;s authoritarian instincts wasn&amp;rsquo;t legal or Constitutional, but rather cultural. The culture of free speech, of being able to criticize&amp;thinsp;&amp;mdash;&amp;thinsp;in no uncertain terms, with no held punches&amp;thinsp;&amp;mdash;&amp;thinsp;anyone in authority is fundamental to the American mindset.
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://www.nytimes.com/2025/09/17/business/media/abc-jimmy-kimmel.html&quot;&gt;John Koblin at The New York Times today&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ABC announced on Wednesday evening that it was pulling Jimmy Kimmel&amp;rsquo;s late night show &amp;ldquo;indefinitely&amp;rdquo; after criticism of comments he made on Monday about the motives of the man who is accused of fatally shooting the conservative activist Charlie Kirk last week.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We are in serious trouble.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Icon Composer Notes</title>
   <link href="https://www.virtualsanity.com/202507/icon-composer-notes/"/>
   <updated>2025-07-10T11:12:41-04:00</updated>
   <id>https://www.virtualsanity.com/202507/icon-composer-notes</id>
   <author>
      <name>John Brayton</name>
      <uri>https://www.virtualsanity.com/</uri>
   </author>
   <content type="html">&lt;p&gt;&lt;img src=&quot;/media/2025-07-09-icon-composer-post/iconcomposerheader.png&quot; width=&quot;1200&quot; height=&quot;630&quot; alt=&quot;&quot;&gt;&lt;/p&gt;

&lt;p&gt;I spent a significant amount of time since WWDC trying to understand how to correctly use Icon Composer, and determining how best to carry forward &lt;a href=&quot;https://www.goldenhillsoftware.com/unread/&quot;&gt;Unread&lt;/a&gt;’s alternate icons feature with this year’s changes.&lt;/p&gt;
&lt;h3&gt;Thank You&lt;/h3&gt;
&lt;p&gt;I want to thank &lt;a href=&quot;https://kayathomas.is/&quot;&gt;Kaya Thomas&lt;/a&gt;, who helped me understand some aspects of Icon Composer that were not initially clear to me. I also want to thank the Apple engineers who helped me in two different lab sessions.&lt;/p&gt;
&lt;h3&gt;A Quick Tutorial to Using Icon Composer&lt;/h3&gt;
&lt;p&gt;I would not have figured out how to use this tool without help, so I wanted to pass along the correct way to use it. I will use Unread’s icon as an example, since it is a simple icon with three single-color shapes.&lt;/p&gt;
&lt;h4&gt;Step 1. Start with the shape.&lt;/h4&gt;
&lt;p&gt;Start with the shape as a 1024x1024 vector image file, with a white foreground color and a transparent background.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/media/2025-07-09-icon-composer-post/1-starting.png&quot; width=&quot;125&quot; height=&quot;125&quot; alt=&quot;Unread’s icon, 3 solid color shapes, on a transparent background. The foreground color of all the shapes is white.&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;Just pretend the light gray color is white. I needed the white to be visible regardless of the webpage background color.&lt;/i&gt;&lt;/p&gt;
&lt;h4&gt;Step 2. Divide the icon up into individual layers.&lt;/h4&gt;
&lt;p&gt;Divide the icon up into individual layers. The shape should be a solid white. Make the foreground color white and the background color transparent. Export each as a PNG file.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Important:&lt;/b&gt; The WWDC “Create icons with Icon Composer” talk recommends using SVG files. If I follow that advice and use SVG files, I do not get the liquid glass effects as expected. I recommend using PNG files instead.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/media/2025-07-09-icon-composer-post/2-outer.png&quot; width=&quot;125&quot; height=&quot;125&quot; alt=&quot;The outer curve of Unread’s icon&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/media/2025-07-09-icon-composer-post/2-inner.png&quot; width=&quot;125&quot; height=&quot;125&quot; alt=&quot;The inner curve of Unread’s icon&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/media/2025-07-09-icon-composer-post/2-dot.png&quot; width=&quot;125&quot; height=&quot;125&quot; alt=&quot;The dot in Unread’s icon&quot;&gt;&lt;/p&gt;
&lt;h4&gt;Step 3. Create a new icon in Icon Composer.&lt;/h4&gt;
&lt;p&gt;Open Icon Composer and create a new icon.&lt;/p&gt;
&lt;h4&gt;Step 4. Set the background color for the light mode version.&lt;/h4&gt;
&lt;p&gt;You can set &lt;i&gt;Fill&lt;/i&gt; to Automatic, Solid, Gradient, System Light, or System Dark.&lt;/p&gt;
&lt;p&gt;&lt;i&gt;Automatic&lt;/i&gt; seems to mean &lt;i&gt;System Light&lt;/i&gt; when in light mode, and &lt;i&gt;System Dark&lt;/i&gt; when in dark mode. &lt;i&gt;System Light&lt;/i&gt; is a near-white color. &lt;i&gt;System Dark&lt;/i&gt; is a near-black color.&lt;/p&gt;
&lt;p&gt;If you choose &lt;i&gt;Gradient&lt;/i&gt; you can set the starting and ending colors. If you choose &lt;i&gt;Solid&lt;/i&gt; you can choose the solid color.&lt;/p&gt;
&lt;p&gt;I will choose &lt;i&gt;System Light&lt;/i&gt; here, mostly because I want to demonstrate how to set the light mode and dark mode background colors separately.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/media/2025-07-09-icon-composer-post/4-backgroundcolor-lightmode.png&quot; width=&quot;1265&quot; height=&quot;719&quot; alt=&quot;A screenshot of the Unread Composer view of a new icon. An arrow points to the Fill section in the upper right corner of the window.&quot;&gt;&lt;/p&gt;
&lt;h4&gt;Step 5. Set the background color for the dark mode version.&lt;/h4&gt;
&lt;p&gt;First, click the Dark Mode icon in the lower left corner.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/media/2025-07-09-icon-composer-post/5-backgroundcolor-darkmode.png&quot; width=&quot;1265&quot; height=&quot;719&quot; alt=&quot;Points to the Dark mode setting at the lower right portion of the canvas.&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then, at the very top right, set &lt;i&gt;Color&lt;/i&gt; to &lt;i&gt;Dark&lt;/i&gt;. This will make the color selection apply only to the dark mode version.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/media/2025-07-09-icon-composer-post/5-backgroundcolor-dark.png&quot; width=&quot;1265&quot; height=&quot;719&quot; alt=&quot;Points to the Color picker at the top right portion of the inspector pane on the right.&quot;&gt;&lt;/p&gt;
&lt;p&gt;Finally, choose a Fill setting. Since &lt;i&gt;Color&lt;/i&gt; is &lt;i&gt;Dark&lt;/i&gt;, this setting will apply only to the dark mode version. I will set this to System Dark.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/media/2025-07-09-icon-composer-post/5-backgroundcolor-fill.png&quot; width=&quot;1265&quot; height=&quot;719&quot; alt=&quot;Points to the Color picker at the top right portion of the inspector pane on the right.&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Important:&lt;/b&gt; This step illustrates a concept that I did not immediately understand. When you want to work with the light mode, dark mode, or mono version of the icon, you select it in the lower right portion of the canvas.&lt;/p&gt;
&lt;p&gt;When you want to make a color change specific to the light, dark, or mono version, choose it in that &lt;i&gt;Color&lt;/i&gt; menu at the top of the inspector pane on the right.&lt;/p&gt;
&lt;p&gt;Each group of settings in the inspector pane on the right has its own menu. In this set of steps I work exclusively with the &lt;i&gt;Color&lt;/i&gt; menu, but there are others as well.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/media/2025-07-09-icon-composer-post/aside-menu.png&quot; width=&quot;1265&quot; height=&quot;719&quot; alt=&quot;A group of layers is selected in the sidebar. Arrows point to a Color picker, a Liquid Glass picker, and a Composition picker. Each of the three pickers is in the inspector pane on the right. Since the Default (light mode) icon is chosen, each picker lets you choose between All and Default.&quot;&gt;&lt;/p&gt;
&lt;h4&gt;Step 6: Add the layer PNG files.&lt;/h4&gt;
&lt;p&gt;Drag and drop the layer PNG files created in step 2 onto the icon.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/media/2025-07-09-icon-composer-post/6-draglayers.png&quot; width=&quot;1265&quot; height=&quot;719&quot; alt=&quot;The window shows the layers added to the icon. In the sidebar there is a Group with the three layers. In the content pane the icon has the shapes of Unread’s icon with a white foreground color.&quot;&gt;&lt;/p&gt;
&lt;h4&gt;Step 7: Set the default/light mode color for a layer.&lt;/h4&gt;
&lt;p&gt;Select Default in the lower right portion of the canvas. Select a layer in the sidebar. Choose “Default” in the Color picker at the top of the inspector. Then choose a color in the Fill picker in the inspector pane on the right.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/media/2025-07-09-icon-composer-post/7-foreground-layer-lightmode.png&quot; width=&quot;1265&quot; height=&quot;719&quot; alt=&quot;Points to the Default icon in the lower right portion of the canvas. Also points to the Color picker and the Fill picker in the inspector pane for a layer for the default (light mode) icon.&quot;&gt;&lt;/p&gt;
&lt;h4&gt;Step 8: Set the dark mode color for that layer.&lt;/h4&gt;
&lt;p&gt;Select Dark in the lower right portion of the canvas. Choose “Dark” in the Color picker at the top of the inspector. Select the same layer on the left. Then choose a color in the Fill picker in the inspector pane on the right.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/media/2025-07-09-icon-composer-post/8-foreground-layer-darkmode.png&quot; width=&quot;1265&quot; height=&quot;719&quot; alt=&quot;Points to the Dark mode icon in the lower right portion of the canvas. Also points to the Color picker and the Fill picker in the inspector pane for a layer for the dark mode icon.&quot;&gt;&lt;/p&gt;
&lt;h4&gt;Step 9. Set the mono color and opacity for that layer.&lt;/h4&gt;
&lt;p&gt;Select Mono in the lower right portion of the canvas. Choose “Mono” in the Color picker at the top of the inspector.&lt;/p&gt;
&lt;p&gt;It is monochrome, so I think it is best to set the color to pure white. If you want the layer to be dimmer than others, set the opacity to something less than 100%.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/media/2025-07-09-icon-composer-post/9-mono-color-and-opacity.png&quot; width=&quot;1265&quot; height=&quot;719&quot; alt=&quot;Points to the Mono icon in the lower right portion of the canvas. Also points to the Color picker and the Fill picker in the inspector pane for a layer.&quot;&gt;&lt;/p&gt;
&lt;h4&gt;Step 10. Repeat for other layers.&lt;/h4&gt;
&lt;p&gt;Repeat steps 7 through 9 for each of the other layers.&lt;/p&gt;
&lt;p&gt;If you look at the icons in the lower right portion of the canvas, you can see that each now looks reasonable.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/media/2025-07-09-icon-composer-post/10-other-layers.png&quot; width=&quot;1265&quot; height=&quot;719&quot; alt=&quot;The Icon Composer window showing that colors for each layer in each icon version have been specified.&quot;&gt;&lt;/p&gt;
&lt;h4&gt;Step 11: Adjust effects on all layers in the group.&lt;/h4&gt;
&lt;p&gt;When I imported the layers, Icon Composer put them into a group. When I select that group, I can change various effects for all layers in the group. I can also move layers into different groups.&lt;/p&gt;
&lt;p&gt;Above each group of options in the inspector pane is a menu letting you determine whether the settings should apply to all versions or just the currently selected version (default, dark mode, or mono).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/media/2025-07-09-icon-composer-post/11-group-settings.png&quot; width=&quot;1265&quot; height=&quot;719&quot; alt=&quot;The icon in Icon Composer with the Group item selected in the sidebar. The inspector pane contains a variety of settings that apply to the group of layers.&quot;&gt;&lt;/p&gt;
&lt;h4&gt;Step 12: Save the file.&lt;/h4&gt;
&lt;p&gt;Choose Save from the File menu to save the icon as a &lt;code&gt;.icon&lt;/code&gt; file. Then add that icon to the Xcode project.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Important:&lt;/b&gt; Do not choose Export from the File menu. That will save to a PNG file. That will be useful in other contexts, but not when adding the default icon to the app.&lt;/p&gt;
&lt;h3&gt;The .icon file format&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;.icon&lt;/code&gt; file is a package (a folder that convinces Finder to treat it like a single file). You can open it in Finder by right clicking and choosing Show Package Contents from the resulting menu.&lt;/p&gt;
&lt;p&gt;When you do that, you will see that your layer images are saved in an Assets subfolder. The structure of the icon itself is specified in an &lt;code&gt;icon.json&lt;/code&gt; file. Unread has 32 different alternate icon options. Each icon has the same shapes but uses different color combinations. I plan to automate generating these icon files based on lists of color combinations.&lt;/p&gt;
&lt;h3&gt;Alternate Icons on iOS&lt;/h3&gt;
&lt;p&gt;If your iOS app supports alternate icons, you set icons the same way you did with the prior icon format. Add the &lt;code&gt;.icon&lt;/code&gt; files to the Xcode project. The alternate icons still need to be specified the same way in the &lt;code&gt;Info.plist&lt;/code&gt; file. The &lt;i&gt;Include All App Icon Assets&lt;/i&gt; setting still needs to be set to &lt;i&gt;Yes&lt;/i&gt; in Xcode Build Settings.&lt;/p&gt;
&lt;p&gt;If you let customers choose an alternate icon, you probably need to display the different icons in your app. I did not have success reading icons with &lt;code&gt;UIImage(named: String)&lt;/code&gt;. One could draw the icons in code, but mimicking the different effects on the image would be difficult. I think the best way for most is to export the icons to PNG files, and use those PNG files in the app to display the icons.&lt;/p&gt;
&lt;p&gt;There is still no way to let a customer choose one icon for light mode and a different icon for dark mode.&lt;/p&gt;
&lt;p&gt;You can export an icon to PNG file by choosing Export from the File menu.&lt;/p&gt;
&lt;h3&gt;Alternate Icons on macOS&lt;/h3&gt;
&lt;p&gt;On macOS, one sets an alternate icon by either setting &lt;code&gt;NSApplication.shared.applicationIconImage&lt;/code&gt; to an &lt;code&gt;NSImage&lt;/code&gt; or drawing it in code using the &lt;a href=&quot;https://developer.apple.com/documentation/appkit/nsdocktile&quot;&gt;NSDockTile API&lt;/a&gt;. A Mac app can get the current &lt;i&gt;Icon &amp; widget style&lt;/i&gt; setting with &lt;code&gt;UserDefaults.standard.string(forKey: &amp;quot;AppleIconAppearanceTheme&amp;quot;)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In the first developer beta of Tahoe I could create an image using &lt;code&gt;NSImage(named: String)&lt;/code&gt; and display it in the app with an &lt;code&gt;NSImageView&lt;/code&gt;. If the icon had any variation between the light mode version, dark mode version, or mono version, the image drawn would be the mono version. As of the third developer beta, creating an image with &lt;code&gt;NSImage(named: String)&lt;/code&gt; with an icon file does not work at all.&lt;/p&gt;
&lt;p&gt;When I add multiple &lt;code&gt;.icon&lt;/code&gt; files to a Mac app, Xcode seems to only include the app’s default icon. It seems to ignore the others. The icon is included as a &lt;code&gt;.icns&lt;/code&gt; file that appears to be generated from the &lt;code&gt;.icon&lt;/code&gt; file. It is &lt;i&gt;probably&lt;/i&gt; possible to generate those &lt;code&gt;.icns&lt;/code&gt; files some other way and include those as resources.&lt;/p&gt;
&lt;p&gt;The content view for an NSDockTile is always 128x128. Therefore if you decide to store the icons as PNG files, 256x256 image files should be sufficient.&lt;/p&gt;
&lt;p&gt;Alternative dock icons for Mac apps are only used when the app is running.&lt;/p&gt;
&lt;h3&gt;Automating Exports of Icon Files to PNG Files&lt;/h3&gt;
&lt;p&gt;Icon Composer contains an &lt;code&gt;ictool&lt;/code&gt; executable that you can use on the command line to generate PNG files from &lt;code&gt;.icon&lt;/code&gt; files created with Icon Composer. It is at this path under the Xcode app bundle:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Contents/Applications/Icon Composer.app/Contents/Executables/ictool&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;From a directory containing an &lt;code&gt;unread.icon&lt;/code&gt; icon file, I was able to make &lt;code&gt;ictool&lt;/code&gt; generate a 256x256 PNG file for macOS by invoking it as follows:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&quot;/Applications/Xcode-26.4-Release-Candidate.app/Contents/Applications/Icon Composer.app/Contents/Executables/ictool&quot; unread.icon --export-preview macOS Light 256 256 1 unread.png
&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;Supporting Old Versions of the Operating Systems&lt;/h3&gt;

&lt;p&gt;For iOS, Xcode automatically generates a flatter version of the app icon for iOS 18 and earlier. I did not find a way to use an entirely different version of an app icon on iOS 18 and earlier.&lt;/p&gt;

&lt;p&gt;&lt;del&gt;On macOS, it seems that you need to &lt;i&gt;also&lt;/i&gt; have the app icon in the asset catalog. Sequoia or earlier will use the icon from the asset catalog. Tahoe will use the icon from the &lt;code&gt;.icon&lt;/code&gt; file.&lt;/del&gt; This behavior appears to have changed since I wrote this. Michael Tsai has a &lt;a href=&quot;https://mjtsai.com/blog/2025/08/08/separate-icons-for-macos-tahoe-vs-earlier/&quot;&gt;good round-up of information about this&lt;/a&gt;.&lt;/p&gt;


&lt;h3&gt;My Plan for Unread&lt;/h3&gt;
&lt;p&gt;Today icon selection in Unread works as follows:&lt;/p&gt;
&lt;p&gt;On Mac, the customer can choose between one of 32 different icons.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/media/2025-07-09-icon-composer-post/unread-mac-dockicon.png&quot; width=&quot;832&quot; height=&quot;561&quot; alt=&quot;Unread settings window on Mac letting the customer choose 1 of 32 different icons.&quot;&gt;&lt;/p&gt;
&lt;p&gt;On iOS, the default icon has a light mode version and a dark mode version. All icons have the same mono version for tinting.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/media/2025-07-09-icon-composer-post/unread-ios-icon.png&quot; width=&quot;1374&quot; height=&quot;1374&quot; alt=&quot;Unread screen on iPhone letting the customer choose between the default icon (with a light mode and dark mode version) and between 1 of 32 custom icons.&quot;&gt;&lt;/p&gt;
&lt;p&gt;My plan for Unread is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make the default icon on Mac the same as it is on iOS with a light mode version and a dark mode version.
&lt;li&gt;Each of the 32 alternative icons on Mac will have a constant color palette, without light, dark, or mono versions.
&lt;li&gt;Automate generating each of the 32 alternate &lt;code&gt;.icon&lt;/code&gt; files and their exported &lt;code&gt;.png&lt;/code&gt; files.
&lt;/ul&gt;
&lt;h3&gt;Feedback Assistant Reports&lt;/h3&gt;
&lt;p&gt;If anyone at Apple happens to read this, these are relevant Feedback Assistant reports:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;FB18097334: Icons with SVG layers do not get the liquid glass effects as expected
&lt;li&gt;FB18096324: macOS needs an equivalent to UIApplication.shared.setAlternateIconName(…)
&lt;li&gt;FB18093372: In macOS apps, no way to include additional Icon Composer-generated .icon files
&lt;li&gt;FB18091397: On macOS, showing an Icon Composer-generated icon in the app via NSImage and NSImageView shows the monochrome version
&lt;li&gt;FB18027769: Creating a UIImage from a .icon file created with Icon Composer does not work
&lt;li&gt;FB13999626: iOS Home Screen Personalization and Alternate Icons
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;b&gt;Update 2025-06-22:&lt;/b&gt; Added note that an alternative icon on macOS can be set by setting &lt;code&gt;NSApplication.shared.applicationIconImage&lt;/code&gt; to an &lt;code&gt;NSImage&lt;/code&gt;, and that alternative icons for Mac apps are only shown in the dock while the app is running.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Update 2025-07-10:&lt;/b&gt; Posted this as a new article on my longform blog, copying it from my microblog. Added the &amp;ldquo;Supporting Old Versions of the Operating Systems&amp;rdquo; section. Noted that creating an &lt;code&gt;NSImage&lt;/code&gt; from a &lt;code&gt;.icon&lt;/code&gt; no longer works in the third developer beta of Tahoe.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Update 2025-08-11:&lt;/b&gt; Changed the &lt;i&gt;Supporting Old Versions of the Operating Systems&lt;/i&gt; section to reflect changes in newer betas of Xcode and Icon Composer.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Update 2025-08-25:&lt;/b&gt; I originally stated that there was no way to get the current system &lt;i&gt;Icon &amp;amp; widget style&lt;/i&gt; setting. I updated this to reflect that an app can get this by checking &lt;code&gt;UserDefaults.standard.string(forKey: &amp;quot;AppleIconAppearanceTheme&amp;quot;)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Update 2026-03-19:&lt;/b&gt; The command line tool to export icons as PNG files was renamed to &lt;code&gt;ictool&lt;/code&gt; since I originally wrote this post. I updated this post to reflect that.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Reading App Store Reviews with an RSS Reader</title>
   <link href="https://www.virtualsanity.com/201803/reading-app-store-reviews-with-an-rss-reader/"/>
   <updated>2018-03-14T11:26:17-04:00</updated>
   <id>https://www.virtualsanity.com/201803/reading-app-store-reviews-with-an-rss-reader</id>
   <author>
      <name>John Brayton</name>
      <uri>https://www.virtualsanity.com/</uri>
   </author>
   <content type="html">&lt;p&gt;A recent conversation on the &lt;a href=&quot;https://coreint.org/&quot;&gt;Core Intuition&lt;/a&gt; Slack channel got me thinking about keeping up with customer App Store reviews via RSS. Apple provides Atom feeds for reviews, but to get all reviews you need to subscribe to one feed per storefront (country). Apple&amp;rsquo;s feeds include star ratings, but not in a manner that an aggregation service will absorb or that a reader will display. Finally, the feed names do not include the product name, so it is not always apparent what app a review is discussing.&lt;/p&gt;

&lt;p&gt;I took some time to write a script to address this. The script combines all recent reviews of an app into a single JSON Feed. It embeds the star rating into the review content. It also embeds a Google Translate link into the content so that if the review is in a foreign language I can get a translation with one tap. My web server runs the script periodically and I subscribe to the resulting feeds.&lt;/p&gt;

&lt;p&gt;The script is &lt;a href=&quot;https://github.com/jbrayton/app-store-review-feed&quot;&gt;available on GitHub&lt;/a&gt; for anyone else who might find it useful.&lt;/p&gt;

</content>
 </entry>
  
</feed>