fleetingpixels.com Ads.txt file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="og:site_name" content="Fleetings Pixels"/>
<link rel="canonical" href="https://fleetingpixels.com"/>
<meta name="twitter:url" content="https://fleetingpixels.com"/>
<meta name="og:url" content="https://fleetingpixels.com"/>
<title>Coding & Tech with David Caddy | Fleetings Pixels</title>
<meta name="twitter:title" content="Coding & Tech with David Caddy | Fleetings Pixels"/>
<meta name="og:title" content="Coding & Tech with David Caddy | Fleetings Pixels"/>
<meta name="description" content="Coding & Tech with David Caddy"/>
<meta name="twitter:description" content="Coding & Tech with David Caddy"/>
<meta name="og:description" content="Coding & Tech with David Caddy"/>
<meta name="twitter:card" content="summary"/>
<link rel="stylesheet" href="/styles.css" type="text/css"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="shortcut icon" href="/Images/favicon.png" type="image/png"/>
<link rel="alternate" href="/feed.rss" type="application/rss+xml" title="Subscribe to Fleetings Pixels"/>
</head>
<body>
<header>
<div class="wrapper">
<nav>
<ul>
<li>
<a href="/articles">Articles</a></li>
<li>
<a href="/cast">Podcast</a></li>
<li>
<a href="/about">About</a></li></ul></nav>
<a href="/">
<picture>
<source srcset="/Images/headerFleetingPixelsDark.png" media="(prefers-color-scheme: dark)"/>
<img src="/Images/headerFleetingPixelsLight.png" class="header-image"/>
</picture></a></div></header>
<div class="wrapper">
<ul class="item-list">
<li>
<article>
<h1 class="item-list-item-title">
<a href="/articles/2025/nfl-melbourne">NFL Coming to Melbourne: Rams to Play at the Iconic MCG</a></h1>
<div class="published-date">David Caddy | Published February 6, 2025</div>
<div class="content"><p>The National Football League (NFL) <a href="https://www.nfl.com/news/melbourne-to-host-first-ever-nfl-regular-season-game-in-australia-in-2026-rams-designated-team">has announced</a> an exciting development for Australian sports fans. In 2026, a regular season NFL game will be played at the Melbourne Cricket Ground (MCG) with the Los Angeles Rams as the designated team.</p><p>The MCG, also known as "The G," is a legendary sports stadium located in Yarra Park, Melbourne. It is the largest stadium in the Southern Hemisphere and has hosted numerous historic sporting events, including the 1956 Summer Olympics.</p><p>This announcement highlights the NFL's commitment to bringing the excitement of American football to new markets and fans around the world. It's a win for my home city Melbourne and for Aussie sports enthusiasts. Here's hoping the mighty Minnesota Vikings will be the other team travelling down under.</p></div>
<ul class="tag-list">
<li>
<a href="/tags/nfl">NFL</a></li>
<li>
<a href="/tags/mcg">MCG</a></li>
<li>
<a href="/tags/melbourne">Melbourne</a></li></ul></article></li>
<li>
<article>
<h1 class="item-list-item-title">
<a href="/articles/2023/dc-settings">Introducing DCSettings</a></h1>
<div class="published-date">David Caddy | Published May 22, 2023</div>
<div class="content"><p>DCSettings is a Swift package that simplifies the configuration of user preferences with an easy-to-use result builder syntax and a drop-in SwiftUI user interface.</p><p>You can quickly configure settings in <code>UserDefaults</code>, <code>NSUbiquitousKeyValueStore</code> or a custom key-value store like so:</p><pre><code><span class="type">DCSettingsManager</span>.<span class="property">shared</span>.<span class="call">configure</span> {
<span class="type">DCSettingGroup</span>(<span class="string">"General"</span>) {
<span class="type">DCSetting</span>(key: <span class="string">"refreshInterval"</span>) {
<span class="type">DCSettingOption</span>(value: <span class="number">5</span>, label: <span class="string">"5 mins"</span>)
<span class="type">DCSettingOption</span>(value: <span class="number">10</span>, label: <span class="string">"10 mins"</span>)
<span class="type">DCSettingOption</span>(value: <span class="number">15</span>, label: <span class="string">"15 mins"</span>)
<span class="type">DCSettingOption</span>(value: <span class="number">30</span>, label: <span class="string">"30 mins"</span>, isDefault: <span class="keyword">true</span>)
<span class="type">DCSettingOption</span>(value: <span class="number">60</span>, label: <span class="string">"60 mins"</span>)
}
<span class="type">DCSetting</span>(key: <span class="string">"articleListLayout"</span>) {
<span class="type">DCSettingOption</span>(value: <span class="string">"List"</span>, label: <span class="string">"List"</span>, systemImage: <span class="string">"list.bullet"</span>)
<span class="type">DCSettingOption</span>(value: <span class="string">"Grid"</span>, label: <span class="string">"Grid"</span>, systemImage: <span class="string">"square.grid.2x2"</span>)
}
<span class="type">DCSetting</span>(key: <span class="string">"showImages"</span>, defaultValue: <span class="keyword">true</span>)
<span class="type">DCSetting</span>(key: <span class="string">"showFullContent"</span>, defaultValue: <span class="keyword">false</span>)
<span class="type">DCSetting</span>(key: <span class="string">"markAsReadOnScroll"</span>, defaultValue: <span class="keyword">true</span>)
<span class="type">DCSetting</span>(key: <span class="string">"maxSyncItems"</span>, defaultValue: <span class="number">1000</span>)
}
<span class="type">DCSettingGroup</span>(<span class="string">"Appearance"</span>) {
<span class="type">DCSetting</span>(key: <span class="string">"theme"</span>, label: <span class="string">"Theme"</span>, options: [<span class="string">"Light"</span>, <span class="string">"Dark"</span>], defaultIndex: <span class="number">0</span>)
<span class="type">DCSetting</span>(key: <span class="string">"fontSize"</span>, options: [<span class="number">12</span>, <span class="number">14</span>, <span class="number">16</span>, <span class="number">18</span>, <span class="number">20</span>], defaultIndex: <span class="number">2</span>)
<span class="type">DCSetting</span>(key: <span class="string">"lineSpacing"</span>, defaultValue: <span class="number">1.2</span>, lowerBound: <span class="number">1.0</span>, upperBound: <span class="number">1.6</span>, step: <span class="number">0.1</span>)
<span class="type">DCSetting</span>(key: <span class="string">"highlightColor"</span>, defaultValue: <span class="type">Color</span>.<span class="property">blue</span>)
}
}
</code></pre><p>Once your settings are set up, you can quickly add a settings view to your app using <code>DCSettingsView</code>. This view displays a list of all settings that grouped as configured:</p><img src="/Images/Blog/2023/dcsettings-screenshot.png" alt="DCSettings screenshot"/><p>For more information on how to use DCSettings, check out the <a href="https://github.com/davidcaddy/DCSettings">GitHub repository</a>.</p></div>
<ul class="tag-list">
<li>
<a href="/tags/settings">Settings</a></li>
<li>
<a href="/tags/preferences">Preferences</a></li>
<li>
<a href="/tags/swift-package">Swift Package</a></li></ul></article></li>
<li>
<article>
<h1 class="item-list-item-title">
<a href="/articles/2022/control-size">Control Sizing in SwiftUI</a></h1>
<div class="published-date">David Caddy | Published October 23, 2022</div>
<div class="content"><p>Especially when working on Mac apps, <a href="https://developer.apple.com/documentation/swiftui/view/controlsize(_:"><code>controlSize</code></a>) is a handy property to know about that allows you to change the size of controls within a SwiftUI view. The default size is <code>.regular</code>, but there's also <code>.large</code>, <code>.small</code>, and <code>.mini</code>.</p><p>You may have noticed that in the new System Settings app on macOS Ventura, the switch controls are smaller than they would be by default. This is because the control size has been set to <code>.mini</code>.</p><p>As an example, let's say you have a view that contains a number of controls, like so:</p><pre><code><span class="keyword">struct</span> ControlsView: <span class="type">View</span> {
<span class="keyword">var</span> label: <span class="type">String</span>
<span class="keyword">@State private var</span> value = <span class="number">3.0</span>
<span class="keyword">@State private var</span> selected = <span class="number">1</span>
<span class="keyword">@State private var</span> active = <span class="keyword">true
var</span> body: <span class="keyword">some</span> <span class="type">View</span> {
<span class="type">VStack</span> {
<span class="type">HStack</span> {
<span class="type">Text</span>(label)
<span class="type">Spacer</span>()
}
.<span class="call">foregroundColor</span>(.<span class="dotAccess">secondary</span>)
.<span class="call">italic</span>()
<span class="type">Picker</span>(<span class="string">"Selection"</span>, selection: <span class="property">$selected</span>) {
<span class="type">Text</span>(<span class="string">"option 1"</span>).<span class="call">tag</span>(<span class="number">1</span>)
<span class="type">Text</span>(<span class="string">"option 2"</span>).<span class="call">tag</span>(<span class="number">2</span>)
}
<span class="type">Slider</span>(value: <span class="property">$value</span>, in: <span class="number">1</span>...<span class="number">10</span>) {
<span class="type">Text</span>(<span class="string">"Slider"</span>)
}
<span class="type">HStack</span> {
<span class="type">Toggle</span>(<span class="string">"Check box"</span>, isOn: <span class="property">$active</span>)
<span class="type">Spacer</span>()
<span class="type">Button</span>(<span class="string">"Button"</span>) { }
<span class="type">Spacer</span>()
<span class="type">Toggle</span>(<span class="string">"Switch"</span>, isOn: <span class="property">$active</span>)
.<span class="call">toggleStyle</span>(<span class="type">SwitchToggleStyle</span>())
}
}
}
}
</code></pre><p>Then, sepcifying the control size is as simple as using the <code>.controlSize()</code> modifier on the view, along with the desired size:</p><pre><code><span class="keyword">struct</span> ControlSizeView: <span class="type">View</span> {
<span class="keyword">var</span> body: <span class="keyword">some</span> <span class="type">View</span> {
<span class="type">VStack</span> {
<span class="type">ControlsView</span>(label: <span class="string">"Mini"</span>)
.<span class="call">controlSize</span>(.<span class="dotAccess">mini</span>)
<span class="type">Divider</span>()
<span class="type">ControlsView</span>(label: <span class="string">"Small"</span>)
.<span class="call">controlSize</span>(.<span class="dotAccess">small</span>)
<span class="type">Divider</span>()
<span class="type">ControlsView</span>(label: <span class="string">"Regular"</span>)
.<span class="call">controlSize</span>(.<span class="dotAccess">regular</span>)
<span class="type">Divider</span>()
<span class="type">ControlsView</span>(label: <span class="string">"Large"</span>)
.<span class="call">controlSize</span>(.<span class="dotAccess">large</span>)
}
.<span class="call">padding</span>()
}
}
</code></pre><p>The result running on macOS:</p><img src="/Images/Blog/2022/controlSize.png"/></div>
<ul class="tag-list">
<li>
<a href="/tags/swiftui">SwiftUI</a></li>
<li>
<a href="/tags/control-size">Control Size</a></li>
<li>
<a href="/tags/macos">macOS</a></li></ul></article></li>
<li>
<article>
<h1 class="item-list-item-title">
<a href="/articles/2021/app-analytics-usage-data-for-mac-menu-bar-applications">App Analytics Usage Data for Mac Menu Bar Applications</a></h1>
<div class="published-date">David Caddy | Published June 12, 2021</div>
<div class="content"><p>Back on March 15, 2021, Apple <a href="https://developer.apple.com/news/releases/?id=03162021">put out a release</a> to developers stating:</p><blockquote class="blockquote-normal"><p>App Analytics now provides usage metrics for Mac apps, including data on installations, sessions, active devices, crashes, and deletions. And now you can measure user retention to see how often users return to your app after downloading it. Data is only available from users who have agreed to share their diagnostics and usage information with app developers.</p></blockquote><p>As with most things to do with <a href="https://developer.apple.com/app-store-connect/analytics/">App Analytics</a>, Apple has done a fantastic job of enabling this without any action required from developers. However if you, like me, have been puzzled to not see this new data come through for your menu bar apps, I learned from a helpful <a href="https://developer.apple.com/wwdc21/">WWDC</a> lab session that agent applications (<strong>LSUIElement</strong>) are excluded from these new metrics.</p><p>Admittedly this use case is more challenging to capture in a meaningful way, but menu bar apps are different to entirely background processes in that the user does interact with them on screen, so I’m hopefully some form of usage metrics will be enabled for this class of apps in the future.</p></div>
<ul class="tag-list">
<li>
<a href="/tags/wwdc">WWDC</a></li>
<li>
<a href="/tags/analytics">Analytics</a></li>
<li>
<a href="/tags/menu-bar">Menu Bar</a></li></ul></article></li>
<li>
<article>
<h1 class="item-list-item-title">
<a href="/articles/2021/pop-up-buttons-in-mac-catalyst-v3">Pop-Up Buttons in Mac Catalyst v3</a></h1>
<div class="published-date">David Caddy | Published June 10, 2021</div>
<div class="content"><p>Starting with macOS Monterey, Catalyst apps will now be able to use pop-up buttons.</p><img src="/Images/Blog/2021/pop-up-button-example.gif"/><p>Simply set a menu to the target button and enable both <strong>showsMenuAsPrimaryAction</strong> and <strong>changesSelectionAsPrimaryAction</strong>:</p><pre><code><span class="keyword">let</span> button = <span class="type">UIButton</span>(type: .<span class="dotAccess">system</span>)
button.<span class="property">menu</span> = <span class="type">UIMenu</span>()
button.<span class="property">showsMenuAsPrimaryAction</span> = <span class="keyword">true</span>
button.<span class="property">changesSelectionAsPrimaryAction</span> = <span class="keyword">true</span>
</code></pre></div>
<ul class="tag-list">
<li>
<a href="/tags/macos">macOS</a></li>
<li>
<a href="/tags/catalyst">Catalyst</a></li>
<li>
<a href="/tags/wwdc">WWDC</a></li></ul></article></li>
<li>
<article>
<h1 class="item-list-item-title">
<a href="/articles/2021/tooltips-in-mac-catalyst-v3">Tooltips in Mac Catalyst v3</a></h1>
<div class="published-date">David Caddy | Published June 10, 2021</div>
<div class="content"><p>Starting with macOS Monterey, Catalyst apps will now be able to display tooltips; little messages that display on hover to provide the user with helpful hints and context.</p><img src="/Images/Blog/2021/tooltipExample.png"/><p>Any <strong>UIView</strong> can be given a tooltip using <strong>UIToolTipInteraction</strong> like so:</p><pre><code><span class="keyword">let</span> toolTipView = <span class="type">UIView</span>()
<span class="keyword">let</span> toolTipMessage = <span class="string">"Tool Tip text"</span>
<span class="keyword">let</span> toolTipInteraction = <span class="type">UIToolTipInteraction</span>(defaultToolTip: toolTipMessage)
toolTipView.<span class="call">addInteraction</span>(toolTipInteraction)
</code></pre><p>As one of the most common use cases is to help clarify the function of buttons and other controls, <strong>UIControl</strong> been given a convenience property, that makes it even easier to had tooltips to any control that conforms:</p><pre><code><span class="keyword">let</span> button = <span class="type">UIButton</span>(type: .<span class="dotAccess">system</span>)
button.<span class="property">toolTip</span> = <span class="string">"Tool Tip Text"</span>
</code></pre></div>
<ul class="tag-list">
<li>
<a href="/tags/macos">macOS</a></li>
<li>
<a href="/tags/catalyst">Catalyst</a></li>
<li>
<a href="/tags/wwdc">WWDC</a></li></ul></article></li>
<li>
<article>
<h1 class="item-list-item-title">
<a href="/articles/2021/platform-specific-modifiers-ii-swiftui">Platform Conditional Modifiers in SwiftUI v3</a></h1>
<div class="published-date">David Caddy | Published June 9, 2021</div>
<div class="content"><p>SwiftUI version 3 (coming in iOS 15 & macOS Monterey) has brought with it many nice features, including the ability to use <strong>#if</strong> conditional statements for postfix member expressions and modifiers:</p><pre><code><span class="type">Text</span>(<span class="string">"Title"</span>)
<span class="preprocessing">#if os(iOS)</span>
.<span class="call">font</span>(.<span class="dotAccess">title</span>)
<span class="preprocessing">#else</span>
.<span class="call">font</span>(.<span class="dotAccess">headline</span>)
<span class="preprocessing">#endif</span>
</code></pre></div>
<ul class="tag-list">
<li>
<a href="/tags/swiftui">SwiftUI</a></li>
<li>
<a href="/tags/wwdc">WWDC</a></li>
<li>
<a href="/tags/ios-15">iOS 15</a></li></ul></article></li>
<li>
<article>
<h1 class="item-list-item-title">
<a href="/articles/2021/markdown-in-swiftui-v3">Markdown in SwiftUI v3</a></h1>
<div class="published-date">David Caddy | Published June 9, 2021</div>
<div class="content"><p>SwiftUI version 3 (coming in iOS 15 & macOS Monterey) has brought with it many nice features, including the ability to easily work with <a href="https://en.wikipedia.org/wiki/Markdown">Markdown</a>. <strong>Text</strong> elements now support Markdown natively, like so:</p><pre><code><span class="type">VStack</span> {
<span class="type">Text</span>(<span class="string">"Regular text"</span>)
<span class="type">Text</span>(<span class="string">"*Italic text*"</span>)
<span class="type">Text</span>(<span class="string">"**Bold text**"</span>)
<span class="type">Text</span>(<span class="string">"~~Strikethrough text~~"</span>)
<span class="type">Text</span>(<span class="string">"[Link](apple.com) text"</span>)
}
</code></pre><img src="/Images/Blog/2021/swiftuiMarkdown.png"/></div>
<ul class="tag-list">
<li>
<a href="/tags/swiftui">SwiftUI</a></li>
<li>
<a href="/tags/wwdc">WWDC</a></li>
<li>
<a href="/tags/ios-15">iOS 15</a></li></ul></article></li>
<li>
<article>
<h1 class="item-list-item-title">
<a href="/articles/2021/how-to-load-an-image-asynchronously-in-swiftui-v3">Load Images Asynchronously in SwiftUI v3</a></h1>
<div class="published-date">David Caddy | Published June 9, 2021</div>
<div class="content"><p>SwiftUI version 3 (coming in iOS 15 & macOS Monterey) has brought with it many nice features, including <strong>AsyncImage</strong>, which greatly simplifies the process of downloading and displaying remote images from the internet:</p><pre><code><span class="keyword">let</span> imageURL = <span class="type">URL</span>(string: <span class="string">"<exampleURL>"</span>)
<span class="comment">//...</span>
<span class="type">AsyncImage</span>(url: url) { image <span class="keyword">in</span>
image
.<span class="call">resizable</span>()
.<span class="call">aspectRatio</span>(contentMode: .<span class="dotAccess">fill</span>)
} placeholder: {
<span class="type">Color</span>.<span class="property">red</span>
}
.<span class="call">frame</span>(width: <span class="number">200</span>, height: <span class="number">200</span>)
</code></pre><p>In the above example a red placeholder view is shown while the image is loading.</p><img src="/Images/Blog/2021/asyncImage.gif"/></div>
<ul class="tag-list">
<li>
<a href="/tags/swiftui">SwiftUI</a></li>
<li>
<a href="/tags/wwdc">WWDC</a></li>
<li>
<a href="/tags/ios-15">iOS 15</a></li></ul></article></li>
<li>
<article>
<h1 class="item-list-item-title">
<a href="/articles/2020/baby-yoda-finally-has-a-name-grogu">Baby Yoda Finally Has a Name: Grogu</a></h1>
<div class="published-date">David Caddy | Published November 27, 2020</div>
<div class="content"><p>The oh-so-popular little green character on Disney’s hit TV show <a href="https://en.wikipedia.org/wiki/The_Mandalorian">The Mandalorian</a> finally has a name; “Grogu”. That’s right, we can all finally stop referring to the cute little fella as Baby Yoda or The Child.</p><img src="/Images/Blog/2020/groguBabyYodaTheChild.jpg"/><p>Coming in <a href="https://en.wikipedia.org/wiki/Chapter_13:_The_Jedi">Season 2, Episode 5</a> this name is divulged by Jedi Ahsoka Tano, along with several other revelations of Grogu’s past.</p></div>
<ul class="tag-list">
<li>
<a href="/tags/grogu">Grogu</a></li>
<li>
<a href="/tags/baby-yoda">Baby Yoda</a></li>
<li>
<a href="/tags/the-child">The Child</a></li></ul></article></li>
<li>
<article>
<h1 class="item-list-item-title">
<a href="/articles/2020/how-to-add-chapters-and-other-metadata-to-your-podcasts-on-macos">How to Add Chapters and Other Metadata to Your Podcasts on macOS</a></h1>
<div class="published-date">David Caddy | Published August 6, 2020</div>
<div class="content"><p>If you have a podcast, a Mac and want to take your show to the next level then this post is for you.</p><p>PodWise is a simple and intuitive app for the Mac that allows you to quickly and easily add chapter markers and other metadata to your MP3 episode files (using ID3 tags). Chapters are a fantastic addition to any podcast, especially those that are longer form, and your listeners will thank you for their inclusion.</p><p>PodWise is available now on the <a href="https://apps.apple.com/app/podwise/id1516835651">Mac App Store</a>.</p><p>The video below shows PodWise in action, check it out to see how simple adding a little extra polish to your show can be:</p><p><iframe src="https://www.youtube.com/embed/L5yD7tUt0tg" allow="fullscreen"></iframe></p></div>
<ul class="tag-list">
<li>
<a href="/tags/podcast">Podcast</a></li>
<li>
<a href="/tags/chapters">Chapters</a></li>
<li>
<a href="/tags/id3">ID3</a></li>
<li>
<a href="/tags/mp3">MP3</a></li></ul></article></li>
<li>
<article>
<h1 class="item-list-item-title">
<a href="/articles/2020/developer-sat">Developer Sat</a></h1>
<div class="published-date">David Caddy | Published June 22, 2020</div>
<div class="content"><p>The recent App Store <a href="https://www.theverge.com/2020/6/18/21296180/apple-hey-email-app-basecamp-rejection-response-controversy-antitrust-regulation">rejection of Basecamp's new email app Hey</a> has triggered much overdue conversation around Apple's relationship with third-party developers.</p><p>As a very brief recap of the situation, Apple has a number of rules that developer's must comply with in order for their apps to ever grace a customer's iDevice, including mandatory use of Apple's payment mechanisms when selling any digital goods or services. And while there are <a href="https://youdownloadtheappanditdoesntwork.com/">exceptions</a> if certain conditions are met and hoops jumped through, this issue essentially boils down to Basecamp wanting to charge customer's directly and Apple not allowing it.</p><p>As a developer who's enjoyed some success on iOS, Apple has been good to me, and <a href="https://daringfireball.net/linked/2020/06/19/swisher-app-store-hey">like many</a> I'd prefer not to rock the boat but I would like some choice. And given Apple is one-half of a mobile operating system duopoly with Google – <a href="https://www.theverge.com/2020/6/16/21292651/apple-eu-antitrust-investigation-app-store-apple-pay">currently under investigation</a> by the EU on antitrust concerns – perhaps now is the time for the Cupertino tech giant to revisit some of its policies.</p><p>One part of the controversy revolves around money; Apple takes 30% of all App Store revenue, which some have grumbled about for years and congressman David Cicilline in a <a href="https://www.theverge.com/2020/6/18/21295778/apple-app-store-hey-email-fees-policies-antitrust-wwdc-2020">recent interview</a> labeled as "highway robbery" and "unconscionable". In comparison, <a href="https://9to5mac.com/2019/03/06/microsoft-store-revenue-share/">as of early last year</a>, Microsoft takes 15% of sales revenue on its Windows App Store, and just 5% of those generated from a direct link (presumedly from external marketing exercises from the company behind the product). Another comparison one could make is with content creators and Youtube, where Google takes a massive 45% of advertising revenue on the platform, making 30% all of a sudden seem quite reasonable.</p><p>I think the actual figure here is not the key issue though, it's the fact that the App Store is the only way to distribute iOS software to customers that's the problem. Given the shear size of Apple these days and their immense influence on the technology sector, the company does have a duty to be transparent and competitive in its business operations.</p><p>Imagine a world where Apple takes 5% of App Store sales revenue, would most developers be far happier? Of course. Imagine another world where Apple takes 30% but allows an alternate means to distribute applications, would most be happier then? I think so.</p><p>If Apple does change course towards either scenario it will lose some revenue, and for a for-profit company any move in that direction on the surface doesn't make sense. Third-party developers have been and will continue to be part of Apple's long-term success, however, and how they feel should be important to the company.</p><p>John Gruber discussed an undercurrent of App Store dissatisfaction from developers in <a href="https://daringfireball.net/linked/2020/06/19/swisher-app-store-hey">a piece from earlier this week</a>:</p><blockquote class="blockquote-normal"><p>This resentment runs deep and is stunningly widespread. You have to trust me on the number of stories I’ve been told in confidence, just this week. Again, putting aside everything else — legal questions of antitrust and competition, ethical questions about what’s fair, procedural questions regarding what should change in the written and unwritten App Store rules, acknowledgement of all the undeniably great things about the App Store from the perspective of users and developers — this deep widespread resentment among developers large and small is a serious problem for Apple.</p></blockquote><p>This issue is a complex one and while ultimately I feel Apple can do what it likes, I also think those in charge should reflect on their values and reconsider the company's current position. I'd say it's unlikely but maybe, just maybe, Apple will go some way to addressing these concerns at WWDC, their imminent developers conference.</p><p>I'll leave you with the <a href="https://rustyshelf.org/2020/06/21/hey-we-need-to-talk/">thoughts</a> of Russell Ivanovic, of <a href="https://blog.shiftyjelly.com/">Shifty Jelly</a> and <a href="https://www.pocketcasts.com/">Pocket Casts</a> fame:</p><blockquote class="blockquote-normal"><p>As a developer, who has put up with this for over a decade now, I’m only asking one thing. Please consider how many of us feel this way, and how bad this will be for developers and eventually, customers, if Apple keeps being allowed to move the goal posts further and further without us pushing back.</p></blockquote></div>
<ul class="tag-list">
<li>
<a href="/tags/app-store">App Store</a></li>
<li>
<a href="/tags/hey">Hey</a></li></ul></article></li>
<li>
<article>
<h1 class="item-list-item-title">
<div>
<a href="/articles/2020/apple-redesigns-app-store-connect-portal-with-mobile-website-ahead-of-wwdc-2020">→</a>
<a href="https://9to5mac.com/2020/06/16/apple-redesigns-app-store-connect-portal-with-mobile-website-ahead-of-wwdc-2020/">Apple redesigns App Store Connect portal with mobile website</a></div></h1>
<div class="published-date">David Caddy | Published June 17, 2020</div>
<div class="content"><p>Filipe Espósito reporting for 9To5Mac:</p><blockquote class="blockquote-normal"><p>Following yesterday’s release of a new update to the Apple Developer app with a macOS version, Apple today revamped the App Store Connect portal with a modern design just a few days before WWDC 2020.</p></blockquote><p>Finally! I had lost all hope Apple would ever fix App Store Connect on mobile. I can’t tell you how many frustrated minutes I’ve spent trying to carefully pinch, scroll and zoom, just trying to check or change one simple thing on my phone from bed. This bodes well for more welcome changes for developers come Monday.</p></div>
<ul class="tag-list">
<li>
<a href="/tags/app-store-connect">App Store Connect</a></li>
<li>
<a href="/tags/wwdc">WWDC</a></li></ul></article></li>
<li>
<article>
<h1 class="item-list-item-title">
<a href="/articles/2020/how-to-adopt-uiscene-apis-in-an-existing-ios-project">How to Adopt UIScene APIs in an Existing iOS Project</a></h1>
<div class="published-date">David Caddy | Published June 17, 2020</div>
<div class="content"><p>With iOS 13, Apple introduced the idea of scenes to enable apps having and managing multiple windows. If you create a new Xcode project this new lifecycle paradigm will be set up for you, in this post I’ll walk you through the few simple steps needed to add <strong>UIScene</strong> support to an existing project.</p><p>First, add the following two <strong>UISceneSession</strong> methods to your App Delegate:</p><pre><code><span class="keyword">func</span> application(<span class="keyword">_</span> application: <span class="type">UIApplication</span>, configurationForConnecting connectingSceneSession: <span class="type">UISceneSession</span>, options: <span class="type">UIScene</span>.<span class="type">ConnectionOptions</span>) -> <span class="type">UISceneConfiguration</span> {
<span class="comment">// Called when a new scene session is being created.
// Use this method to select a configuration to create
// the new scene with.</span>
<span class="keyword">return</span> <span class="type">UISceneConfiguration</span>(name: <span class="string">"Default Configuration"</span>, sessionRole: connectingSceneSession.<span class="property">role</span>)
}
<span class="keyword">func</span> application(<span class="keyword">_</span> application: <span class="type">UIApplication</span>, didDiscardSceneSessions sceneSessions: <span class="type">Set</span><<span class="type">UISceneSession</span>>) {
<span class="comment">// Called when the user discards a scene session.
// If any sessions were discarded while the application
// was not running, this will be called shortly after
// application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were
// specific to the discarded scenes, as they will not return.</span>
}
</code></pre><p>Next, create a scene delegate that conforms to <strong>UIResponder</strong> and <strong>UIWindowSceneDelegate</strong>, like so:</p><pre><code><span class="keyword">import</span> UIKit
<span class="keyword">class</span> SceneDelegate: <span class="type">UIResponder</span>, <span class="type">UIWindowSceneDelegate</span> {
<span class="keyword">var</span> window: <span class="type">UIWindow</span>?
<span class="keyword">func</span> scene(<span class="keyword">_</span> scene: <span class="type">UIScene</span>, willConnectTo session: <span class="type">UISceneSession</span>, options connectionOptions: <span class="type">UIScene</span>.<span class="type">ConnectionOptions</span>) {
<span class="comment">// Use this method to optionally configure and attach the
// UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will
// automatically be initialized and attached to the scene.
// This delegate doesn't imply the connecting scene or session are new
// (see `application:configurationForConnectingSceneSession` instead).</span>
<span class="keyword">guard let _</span> = (scene <span class="keyword">as</span>? <span class="type">UIWindowScene</span>) <span class="keyword">else</span> { <span class="keyword">return</span> }
}
<span class="keyword">func</span> sceneDidDisconnect(<span class="keyword">_</span> scene: <span class="type">UIScene</span>) {}
<span class="keyword">func</span> sceneDidBecomeActive(<span class="keyword">_</span> scene: <span class="type">UIScene</span>) {}
<span class="keyword">func</span> sceneWillResignActive(<span class="keyword">_</span> scene: <span class="type">UIScene</span>) {}
<span class="keyword">func</span> sceneWillEnterForeground(<span class="keyword">_</span> scene: <span class="type">UIScene</span>) {}
<span class="keyword">func</span> sceneDidEnterBackground(<span class="keyword">_</span> scene: <span class="type">UIScene</span>) {}
}
</code></pre><p>Once your scene delegate is in place, the project’s Info.plist needs to be edited to reference it:</p><pre><code><key><span class="type">UIApplicationSceneManifest</span></key>
<dict>
<key><span class="type">UIApplicationSupportsMultipleScenes</span></key>
<<span class="keyword">false</span>/>
<key><span class="type">UISceneConfigurations</span></key>
<dict>
<key><span class="type">UIWindowSceneSessionRoleApplication</span></key>
<array>
<dict>
<key><span class="type">UILaunchStoryboardName</span></key>
<string><span class="type">LaunchScreen</span></string>
<key><span class="type">UISceneConfigurationName</span></key>
<string><span class="type">Default Configuration</span></string>
<key><span class="type">UISceneDelegateClassName</span></key>
<string><span class="type">SceneDelegate</span></string>
<key><span class="type">UISceneStoryboardFile</span></key>
<string><span class="type">Main</span></string>
</dict>
</array>
</dict>
</dict>
</code></pre><p>And that’s it, if everything’s hooked up correctly you should now be up and running with scenes.</p></div>
<ul class="tag-list">
<li>
<a href="/tags/swift">Swift</a></li>
<li>
<a href="/tags/uiscene">UIScene</a></li>
<li>
<a href="/tags/uiwindow">UIWindow</a></li></ul></article></li>
<li>
<article>
<h1 class="item-list-item-title">
<a href="/articles/2020/how-to-create-a-mac-menu-bar-app-with-nspopover">How to Create a Mac Menu Bar App With NSPopover</a></h1>
<div class="published-date">David Caddy | Published June 15, 2020</div>
<div class="content"><p>In this tutorial I'm going to walk you through creating a menu bar app for macOS, which displays a custom view inside an <a href="https://developer.apple.com/documentation/appkit/nspopover">NSPopover</a>.</p><img src="/Images/Blog/2020/macMenuBarGoal.png"/><p><em>The goal of this tutorial</em></p><h2>Basic Set Up</h2><p>In Xcode, go to File > New > Project… then select <em>App</em> from the <em>macOS</em> tab and click <em>Next</em>. Fill in the project details as you like, click <em>Next</em> again, choose where to save the project and click <em>Create</em>.</p><p>To be a proper menu bar app, the interface should only be accessible from the menu bar. To archive this we need to hide the dock icon and main window. First, simply open the Main.Storyboard file of the project then select and delete the <em>Window Controller Scene</em> (<strong><em>not</em></strong> the <em>View Controller Scene</em>). This will prevent the main window from displaying.</p><img src="/Images/Blog/2020/macMenuBarScreenshot1.png"/><p>To disable the dock icon, open the Info.plist file of the project and add <strong>Application is agent (UIElement)</strong> as a new key and set its value to <strong>YES</strong>.</p><img src="/Images/Blog/2020/macMenuBarScreenshot2.png"/><p>Before we move on to the fun stuff, let's just take care of one more thing. In order to display an <strong>NSPopover</strong>, we will need a reference to an <strong>NSViewController</strong>, so let's quickly set up a way to get one.</p><p>Open the Main.Storyboard again, select the <em>View Controller Scene</em> and click the small triangular disclosure button on the left to reveal its <em>View Controller</em> object. Select that, and then in the Identity Inspector (press Option + Command + 4 to show the inspector) make sure the <em>Storyboard ID</em> property is set to "ViewController".</p><p>Now jump over to the ViewController.swift file and add the following inside the <em>ViewController</em> class.</p><pre><code><span class="keyword">static func</span> newInsatnce() -> <span class="type">ViewController</span> {
<span class="keyword">let</span> storyboard = <span class="type">NSStoryboard</span>(name: <span class="type">NSStoryboard</span>.<span class="type">Name</span>(<span class="string">"Main"</span>), bundle: <span class="keyword">nil</span>)
<span class="keyword">let</span> identifier = <span class="type">NSStoryboard</span>.<span class="type">SceneIdentifier</span>(<span class="string">"ViewController"</span>)
<span class="keyword">guard let</span> viewcontroller = storyboard.<span class="call">instantiateController</span>(withIdentifier: identifier) <span class="keyword">as</span>? <span class="type">ViewController</span> <span class="keyword">else</span> {
<span class="call">fatalError</span>(<span class="string">"Unable to instantiate ViewController in Main.storyboard"</span>)
}
<span class="keyword">return</span> viewcontroller
}
</code></pre><p>I won't explain this in great detail as we have bigger fish to fry, suffice to say it's a convenience method we will use later to create an instance of the <em>ViewController</em> class from our main storyboard. Once finished, this class will be the main interface for your app and can be customised as needed.</p><h2>Displaying the Popover</h2><p>Enough of the tedious stuff, let's get a popover going already!</p><p>Go to the AppDelegate.swift file and copy the following inside and at the top of the AppDelegate class.</p><pre><code><span class="keyword">let</span> statusItem = <span class="type">NSStatusBar</span>.<span class="property">system</span>.<span class="call">statusItem</span>(withLength:<span class="type">NSStatusItem</span>.<span class="property">squareLength</span>)
</code></pre><p>This code asks the system to create and return a new, empty status item object of a specified width.</p><blockquote class="blockquote-info"><p>Menu items that occupy the right side of the macOS status bar are called status items, those on the left - used to perform actions in the currently focused application - are just referred to as menu items (<em><a href="https://developer.apple.com/documentation/appkit/nsmenuitem"><strong>NSMenuItem</strong></a></em> and <em><a href="https://developer.apple.com/documentation/appkit/nsstatusitem"><strong>NSStatusItem</strong></a></em> respectively).</p></blockquote><p>For the length parameter we pass in <strong>NSStatusItem.squareLength</strong> to make the item as wide as it is heigh (a.k.a square).</p><p>Now that we have a reference to the newly created status bar item, we can customise its appearance and listen for click events.</p><p>Add the following inside the <em>applicationDidFinishLaunching</em> function, still in AppDelegate.swift.</p><pre><code><span class="keyword">if let</span> button = <span class="keyword">self</span>.<span class="property">statusItem</span>.<span class="property">button</span> {
button.<span class="property">image</span> = <span class="type">NSImage</span>(named: <span class="type">NSImage</span>.<span class="type">Name</span>(<span class="string">"ExampleMenuBarIcon"</span>))
button.<span class="property">action</span> = <span class="keyword">#selector</span>(<span class="type">AppDelegate</span>.<span class="call">togglePopover</span>(<span class="keyword">_</span>:))
}
</code></pre><p>Here we are getting a reference to the <em>NSStatusBarButton</em> object of the status item and setting its image to a custom one.</p><blockquote class="blockquote-alert"><p>Make sure you have an image set inside the Assets.xcassets folder with a @2x image of size 32x32px, and use its name in place of "ExampleMenuBarIcon".</p></blockquote><p>We then set the button's action to trigger the <em>togglePopover</em> function, that we will write next, to fire whenever the status item is clicked.</p><p>Add the following function inside and at the bottom of the <em>AppDelegate</em> class.</p><pre><code><span class="keyword">@objc func</span> togglePopover(<span class="keyword">_</span> sender: <span class="type">NSStatusItem</span>) {
}
</code></pre><p>Now that we are capturing click events, it's finially time to display the promised popover. Add an initialised <strong>NSPopover</strong> variable under the status item declaration, still inside AppDelegate.swift, like so:</p><pre><code><span class="keyword">let</span> popover = <span class="type">NSPopover</span>()
</code></pre><p>And then add the following two lines at the end of the <em>applicationDidFinishLaunching</em> function:</p><pre><code><span class="keyword">self</span>.<span class="property">popover</span>.<span class="property">contentViewController</span> = <span class="type">ViewController</span>.<span class="call">newInsatnce</span>()
<span class="keyword">self</span>.<span class="property">popover</span>.<span class="property">animates</span> = <span class="keyword">false</span>
</code></pre><p>The first line sets the content of the popover to be a new instance of the <em>ViewController</em> class, using the convenience method we set up earlier. The second just prevents the popover from animating when opened or closed, which I find distracting.</p><p>Now add the following function at the end of the on <em>AppDelegate</em> class:</p><pre><code><span class="keyword">func</span> showPopover(sender: <span class="type">Any</span>?) {
<span class="keyword">if let</span> button = <span class="keyword">self</span>.<span class="property">statusItem</span>.<span class="property">button</span> {
<span class="keyword">self</span>.<span class="property">popover</span>.<span class="call">show</span>(relativeTo: button.<span class="property">bounds</span>, of: button, preferredEdge: <span class="type">NSRectEdge</span>.<span class="property">minY</span>)
}
}
<span class="keyword">func</span> closePopover(sender: <span class="type">Any</span>?) {
<span class="keyword">self</span>.<span class="property">popover</span>.<span class="call">performClose</span>(sender)
}
</code></pre><p>As the names suggest, these will be used to actually show and hide the popover.</p><p>Now add the following inside the previously written and empty <em>togglePopover</em> function.</p><pre><code><span class="keyword">if self</span>.<span class="property">popover</span>.<span class="property">isShown</span> {
<span class="call">closePopover</span>(sender: sender)
}
<span class="keyword">else</span> {
<span class="call">showPopover</span>(sender: sender)
}
</code></pre><p>This code will trigger the appropriate action - either open or close - depending on the current status of the popover, creating the toggling behaviour.</p><p>That's it! Now try to build and run. If everything was set up correctly, you should see a status icon with your custom icon, that when clicked shows and hides a view inside a popover.</p><p>Demo code is available <a href="https://github.com/davidcaddy/MenuBarPopoverExample">here</a>, which also includes a simple <em>EventMonitor</em> class that ensures the popover is dismissed when the user clicks outside its bounds. As well as some commented out code that could be used to capture right mouse click events, which you might want use to display a separate view upon detection of that action.</p></div>
<ul class="tag-list">
<li>
<a href="/tags/nspopover">NSPopover</a></li>
<li>
<a href="/tags/nsstatusitem">NSStatusItem</a></li>
<li>
<a href="/tags/cocoa">Cocoa</a></li></ul></article></li>
<li>
<article>
<h1 class="item-list-item-title">
<a href="/articles/2020/how-to-use-swift-packages-in-objective-c-targets">How to Use Swift Packages in Objective-C Targets</a></h1>
<div class="published-date">David Caddy | Published June 14, 2020</div>
<div class="content"><p>If, as I have, you’ve started using <a href="https://www.raywenderlich.com/7242045-swift-package-manager-for-ios">Swift packages</a> to reuse code amongst your projects, you may have wondered if it’s possible to use them in Objective-C projects. Fortunately, the answer is yes, provided you’re using Xcode <strong>11.4</strong> or newer. Just make sure you use the <strong>@import</strong> syntax in your ObjC file, not the older #import style, and it should work as expected.</p><p>One thing to note, I’ve found that autocomplete doesn’t seem to work consistently, which is understandable for such a recent feature addition. Bearing that in mind, if you start to think things aren’t hooked up properly because Xcode doesn’t seem aware of the package contents, just write some code, double check the syntax, build and it should run fine.</p><p>If you’re new to Swift packages in general, also be aware that classes and functions should be marked as public to be visible and usable outside the package, as they are designated as internal by default. And, of course, remember to use the @objc attribute so your Swift code is accessible within the Objective-C runtime.</p></div>
<ul class="tag-list">
<li>
<a href="/tags/swift">Swift</a></li>
<li>
<a href="/tags/spm">SPM</a></li>
<li>
<a href="/tags/objectivec">Objective-C</a></li></ul></article></li></ul>
<p>
<a href="/articles" class="view-all-link">View All</a></p></div>
<div class="wrapper">
<div>
<footer>
<p>
<a href="/donate">Donate</a></p>
<p>
<a href="https://www.youtube.com/c/FleetingPixels">YouTube</a></p>
<p>
<a href="https://www.instagram.com/fleetingpixels/">Instagram</a></p>
<p>
<a href="https://mastodon.social/@fleetingpixels" rel="me">Mastodon</a></p>
<p>
<a href="https://twitter.com/FleetingPixels">Twitter</a></p>
<p>
<a href="/feed.rss">RSS</a></p>
<p class="hidden-link">
<a href="https://mastodon.social/@caddy" rel="me">Mastodon [DC]</a></p></footer>
<footer>
<div class="copyright">Copyright © 2025 David Caddy. All Rights Reserved</div></footer></div></div></body>
</html>