<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
<title>Snippets — FlineDev</title>
<description>Blog, apps, and open source Swift packages by indie developer Cihat Gündüz. Topics: SwiftUI, visionOS, error handling, localization, and more.</description>
<link>https://fline.dev/snippets/</link>
<language>en</language>
<atom:link href="https://fline.dev/snippets/feed.xml" rel="self" type="application/rss+xml"/>
<item>
<title>Vapor&apos;s maximumActiveConnections Is Per Event Loop, Not Per Worker</title>
<link>https://fline.dev/snippets/vapor-maximum-active-connections/</link>
<guid isPermaLink="true">https://fline.dev/snippets/vapor-maximum-active-connections/</guid>
<pubDate>Mon, 29 Dec 2025 00:00:00 +0000</pubDate>
<description><![CDATA[A non-obvious Vapor configuration detail that can cause intermittent 500 errors when your actual connection count exceeds what you think you configured.]]></description>
<content:encoded><![CDATA[<h2 id="the-misleading-configuration">The Misleading Configuration</h2><p>When configuring connection pools in Vapor – whether for Redis, PostgreSQL, or other databases – you encounter a parameter called <code>maximumActiveConnections</code>. The natural assumption is that this sets the total maximum connections for your application. It does not. It sets the maximum per NIO <code>EventLoop</code>.</p><p>This distinction matters enormously in production.</p><h2 id="the-math-that-surprised-me">The Math That Surprised Me</h2><p>On a typical server, Swift NIO creates one <code>EventLoop</code> per CPU core. So the actual maximum connection count is:</p><pre><code>actual max = maximumActiveConnections * CPU cores * number of dynos/instances</code></pre><p>I had configured <code>maximumActiveConnections</code> to 8, thinking I was capping my Redis connections at a reasonable 16 across two dynos. The real number:</p><pre><code>8 (per event loop) * 8 (cores) * 2 (dynos) = 128 potential connections</code></pre><p>That is an order of magnitude more than intended, and it was exceeding my Redis provider’s connection limit during traffic spikes.</p><p><img src="/assets/images/snippets/vapor-maximum-active-connections/code-screenshot.webp" alt="Code showing the connection pool configuration" loading="lazy" /></p><h2 id="the-symptoms">The Symptoms</h2><p>The failure mode is particularly insidious: intermittent 500 errors that only appear under load. During normal traffic, you never hit the real connection limit, so everything works fine. During spikes, connections pile up across all event loops simultaneously, exceeding the provider’s limit. The errors look random and are difficult to reproduce locally because development machines typically have fewer cores.</p><h2 id="how-to-calculate-the-right-value">How to Calculate the Right Value</h2><p>Divide your provider’s connection limit by the total number of event loops across all instances:</p><pre><code class="language-swift">// Provider limit: 40 connections
// 2 dynos * 8 cores = 16 event loops total
// 40 / 16 = 2.5, round down to 2

app.redis.configuration = .init(
   pool: .init(maximumActiveConnections: 2)
)</code></pre><p>This conservative setting finally fixed the longstanding rare 500 errors in TranslateKit that had been puzzling me for months. Always check your cloud provider’s connection limits and do the multiplication before deploying.</p>]]></content:encoded>
</item>
<item>
<title>Apple Implements Feedback Requests -- Filing Reports Works</title>
<link>https://fline.dev/snippets/apple-implements-feedback-requests/</link>
<guid isPermaLink="true">https://fline.dev/snippets/apple-implements-feedback-requests/</guid>
<pubDate>Tue, 10 Jun 2025 00:00:00 +0000</pubDate>
<description><![CDATA[A personal experience of having a Feedback Assistant request implemented in Xcode 26, and tips for writing effective feedback reports.]]></description>
<content:encoded><![CDATA[<h2 id="your-feedback-actually-gets-read">Your Feedback Actually Gets Read</h2><p>There is a common belief in the developer community that filing Feedback Assistant reports (the successor to Radar) is pointless – that reports disappear into a void and nothing ever happens. I had a feature request implemented in Xcode 26, which proves otherwise.</p><p><img src="/assets/images/snippets/apple-implements-feedback-requests/feedback-implemented.webp" alt="Screenshot showing the implemented feedback request" loading="lazy" /></p><p>Seeing a feature you requested show up in a keynote or release notes is a satisfying validation. But more importantly, it demonstrates that Apple’s engineering teams do review and prioritize community feedback, even when they never respond to the report directly.</p><h2 id="tips-for-writing-effective-feedback">Tips for Writing Effective Feedback</h2><p>Not all reports are created equal. Here is what I have found increases the chances of a report being actionable:</p><p><strong>Be specific about the problem.</strong> “Xcode is slow” is not useful. “Xcode’s SwiftUI preview takes 12 seconds to refresh when the file contains more than 3 <code>#Preview</code> blocks” gives engineers something to investigate.</p><p><strong>Include a sample project.</strong> A minimal reproduction project is the single most valuable thing you can attach. If an engineer can build and run your project to see the issue, you have removed the biggest barrier to them investigating it.</p><p><strong>Describe the use case, not just the solution.</strong> Instead of “add a button that does X,” explain why you need it: “When working with large SPM graphs, I need to quickly identify which target a file belongs to because…” This gives the team context to design the right solution, which might be different from what you imagined.</p><p><strong>File during beta season.</strong> The weeks after WWDC are when Apple’s teams are most actively collecting feedback on new features. Reports filed during this window get significantly more attention than those filed in the middle of a release cycle.</p><h2 id="make-it-a-habit">Make It a Habit</h2><p>Every WWDC beta season, set aside time to test your apps against the new OS and Xcode betas. File reports for every issue and every missing feature. Most will not get a response, but some will quietly influence future releases.</p>]]></content:encoded>
</item>
<item>
<title>Use .labelStyle(.iconOnly) Instead of Nesting Image in Button</title>
<link>https://fline.dev/snippets/labelstyle-icononly-swiftui/</link>
<guid isPermaLink="true">https://fline.dev/snippets/labelstyle-icononly-swiftui/</guid>
<pubDate>Sun, 12 Jan 2025 00:00:00 +0000</pubDate>
<description><![CDATA[The proper SwiftUI pattern for icon-only buttons that preserves accessibility without sacrificing readability.]]></description>
<content:encoded><![CDATA[<h2 id="stop-fighting-the-framework">Stop Fighting the Framework</h2><p>A pattern I see constantly in SwiftUI code is manually creating icon-only buttons by putting an <code>Image</code> directly inside a <code>Button</code>‘s label closure. It works visually, but it throws away accessibility information and fights against SwiftUI’s design.</p><h2 id="the-wrong-way">The Wrong Way</h2><pre><code class="language-swift">Button {
   toggleSidebar()
} label: {
   Image(systemName: &quot;sidebar.left&quot;)
}</code></pre><p>This renders a tappable icon, but VoiceOver has no meaningful label to announce. The user hears something like “button” or the raw SF Symbol name, which is not helpful.</p><h2 id="the-right-way">The Right Way</h2><pre><code class="language-swift">Button(&quot;Toggle Sidebar&quot;, systemImage: &quot;sidebar.left&quot;) {
   toggleSidebar()
}
.labelStyle(.iconOnly)</code></pre><p>Or using <code>Label</code> explicitly:</p><pre><code class="language-swift">Button(action: toggleSidebar) {
   Label(&quot;Toggle Sidebar&quot;, systemImage: &quot;sidebar.left&quot;)
}
.labelStyle(.iconOnly)</code></pre><p><img src="/assets/images/snippets/labelstyle-icononly-swiftui/code-comparison.webp" alt="Code comparison showing the wrong way versus the right way" loading="lazy" /></p><h2 id="why-this-matters">Why This Matters</h2><p>The <code>Label</code> view carries both a title and an icon. When you apply <code>.labelStyle(.iconOnly)</code>, SwiftUI hides the title visually but preserves it in the accessibility tree. VoiceOver will announce “Toggle Sidebar, button” – exactly what the user needs to hear.</p><p>This pattern also makes your code more adaptable. If you later decide to show text alongside the icon (for example, in a toolbar on iPad), you just change the label style to <code>.titleAndIcon</code>. No restructuring needed.</p><h2 id="beyond-buttons">Beyond Buttons</h2><p>The same principle applies to any view that accepts a <code>Label</code>: <code>NavigationLink</code>, <code>Toggle</code>, <code>Picker</code>, menu items. Whenever you are tempted to use a bare <code>Image</code>, ask yourself whether a <code>Label</code> with a style modifier would be more appropriate. In almost every case, it is.</p>]]></content:encoded>
</item>
<item>
<title>EditorConfig for Every SwiftPM Package</title>
<link>https://fline.dev/snippets/editorconfig-swiftpm-package/</link>
<guid isPermaLink="true">https://fline.dev/snippets/editorconfig-swiftpm-package/</guid>
<pubDate>Tue, 03 Dec 2024 00:00:00 +0000</pubDate>
<description><![CDATA[Why every Swift package should include an .editorconfig file to enforce consistent indentation across all contributors.]]></description>
<content:encoded><![CDATA[<h2 id="the-invisible-formatting-problem">The Invisible Formatting Problem</h2><p>When multiple people contribute to a Swift package, indentation inconsistencies creep in. One contributor uses 4 spaces, another uses tabs, a third uses 2 spaces. Pull requests become noisy with whitespace-only changes, and the codebase drifts toward an inconsistent mess. The fix is a single file that takes 30 seconds to add.</p><h2 id="the-editorconfig-standard">The .editorconfig Standard</h2><p>EditorConfig is a widely supported standard for defining coding style settings per project. Xcode respects <code>.editorconfig</code> files, so contributors automatically get the correct indentation settings when they open the project – no manual configuration, no documentation to read.</p><p>Here is the <code>.editorconfig</code> I recommend for every SwiftPM package:</p><pre><code>root = true

[*.swift]
indent_style = space
indent_size = 3

[*.{yml,yaml,json}]
indent_style = space
indent_size = 2</code></pre><p>The 3-space indent for Swift is an intentional choice. It is less common than 2 or 4 spaces, but it hits a sweet spot: more readable than 2 spaces for nested closures, less wasteful of horizontal space than 4 spaces. Once you try it, the standard choices start to feel either too cramped or too spread out.</p><h2 id="why-this-beats-global-settings">Why This Beats Global Settings</h2><p>Every developer has their own Xcode indentation preferences configured globally. Without <code>.editorconfig</code>, those global settings apply to every project they open. This means a contributor with 4-space tabs will silently reformat code when they edit a file, even if the project convention is different.</p><p>With <code>.editorconfig</code> in the repository root, Xcode overrides global settings with the project-specific ones. Contributors do not need to change anything – it just works.</p><h2 id="adoption">Adoption</h2><p>Drop this file in your package root and commit it. That is all. There is no build step, no dependency, no configuration beyond the file itself. Most modern editors (VS Code, Vim, Sublime Text) also support EditorConfig, so non-Xcode contributors benefit too.</p>]]></content:encoded>
</item>
<item>
<title>Push Notifications for App Store Reviews</title>
<link>https://fline.dev/snippets/push-notifications-app-store-reviews/</link>
<guid isPermaLink="true">https://fline.dev/snippets/push-notifications-app-store-reviews/</guid>
<pubDate>Thu, 28 Nov 2024 00:00:00 +0000</pubDate>
<description><![CDATA[How to enable push notifications for new App Store reviews in the App Store Connect app so you can respond to user feedback quickly.]]></description>
<content:encoded><![CDATA[<h2 id="a-hidden-feature-in-app-store-connect">A Hidden Feature in App Store Connect</h2><p>The App Store Connect iOS app has a feature that most developers never discover: push notifications for new user reviews. It is turned off by default, and you have to enable it separately for each app, which is probably why so few people know about it.</p><h2 id="how-to-enable-it">How to Enable It</h2><p>Open the App Store Connect app on your iPhone, then navigate to:</p><ol><li><p>Tap your profile icon or go to <strong>Settings</strong></p></li><li><p>Select <strong>Notifications</strong></p></li><li><p>You will see a list of all your apps</p></li><li><p>For each app, toggle on <strong>Customer Reviews</strong></p></li></ol><p>That is it. From now on, you will get a push notification whenever someone leaves a new review for that app.</p><p><video src="/assets/images/snippets/push-notifications-app-store-reviews/demo.mp4" controls muted playsinline></video></p><h2 id="why-this-matters-for-indie-developers">Why This Matters for Indie Developers</h2><p>Responding to App Store reviews quickly has a tangible impact. When a user leaves a negative review about a bug, a fast reply acknowledging the issue (or pointing them to a fix) can lead them to update their rating. Positive reviews also deserve acknowledgment – it encourages users to keep providing feedback.</p><p>Without notifications, most developers only check reviews when they remember to, which might be days or weeks later. By then, the user has moved on, and your response feels like an afterthought.</p><h2 id="a-word-of-caution">A Word of Caution</h2><p>If you have multiple apps with high review volume, enabling this for all of them could become noisy. Start with your most important apps and adjust based on how many notifications you actually receive. For most indie developers with a handful of apps, the volume is perfectly manageable and the benefits are immediate.</p>]]></content:encoded>
</item>
<item>
<title>Videos and Tabs in DocC Documentation</title>
<link>https://fline.dev/snippets/docc-videos-tabs-documentation/</link>
<guid isPermaLink="true">https://fline.dev/snippets/docc-videos-tabs-documentation/</guid>
<pubDate>Thu, 20 Jun 2024 00:00:00 +0000</pubDate>
<description><![CDATA[Two lesser-known DocC features that make your documentation more interactive: embedded videos and tabbed content navigation.]]></description>
<content:encoded><![CDATA[<h2 id="beyond-basic-markdown-in-docc">Beyond Basic Markdown in DocC</h2><p>Most developers know DocC supports standard markdown, but there are two powerful directives that are surprisingly underused: video embedding and tabbed content. Both work in Xcode’s documentation viewer and on hosted DocC websites.</p><h2 id="embedding-videos">Embedding Videos</h2><p>Adding a video to your documentation is a single directive:</p><pre><code>@Video(source: &quot;setup-walkthrough.mp4&quot;)</code></pre><p>Place the video file in your documentation catalog’s Resources folder. This renders an inline video player directly in the documentation, which is far more effective than linking to an external URL or describing a visual process in text. It works well for setup guides, UI walkthroughs, or demonstrating animations.</p><h2 id="tabbed-content-with-tabnavigator">Tabbed Content with TabNavigator</h2><p>When you need to show alternative approaches or platform-specific instructions, tabs keep things organized without overwhelming the reader:</p><pre><code>@TabNavigator {
   @Tab(&quot;SwiftUI&quot;) {
      Use the `.environment` modifier to inject dependencies.
   }
   @Tab(&quot;UIKit&quot;) {
      Override `viewDidLoad` and configure your dependencies there.
   }
}</code></pre><p>This renders as a proper tabbed interface where readers can switch between sections. It is especially useful for documentation that covers multiple platforms, API versions, or configuration approaches.</p><p><video src="/assets/images/snippets/docc-videos-tabs-documentation/demo.mp4" controls muted playsinline></video></p><h2 id="practical-advice">Practical Advice</h2><p>I updated my Contributing guide to use both of these features, and the result is noticeably more approachable than a wall of text. The video shows the setup process that would take paragraphs to describe, and the tabs separate platform-specific steps cleanly.</p><p>These directives are documented in Apple’s <a href="https://www.swift.org/documentation/docc/video">DocC documentation</a> but rarely mentioned in tutorials. If you maintain an open-source Swift package, consider adding them to your documentation catalog – they make a real difference in how people experience your docs.</p>]]></content:encoded>
</item>
<item>
<title>Convert Paid Apps to Freemium Without Affecting Existing Users</title>
<link>https://fline.dev/snippets/convert-paid-apps-freemium/</link>
<guid isPermaLink="true">https://fline.dev/snippets/convert-paid-apps-freemium/</guid>
<pubDate>Thu, 28 Mar 2024 00:00:00 +0000</pubDate>
<description><![CDATA[How to use StoreKit's AppTransaction API to transition from paid-up-front to freemium while preserving access for users who already paid.]]></description>
<content:encoded><![CDATA[<h2 id="the-paid-to-freemium-transition-problem">The Paid-to-Freemium Transition Problem</h2><p>Switching a paid app to freemium is a common business decision, but it comes with a fairness challenge: users who already paid for the app should not suddenly lose features or be asked to pay again. StoreKit’s <code>AppTransaction</code> API solves this cleanly.</p><h2 id="using-apptransaction-to-check-purchase-history">Using AppTransaction to Check Purchase History</h2><p>The key is <code>AppTransaction.shared</code>, which provides information about the original transaction for your app. Specifically, <code>originalAppVersion</code> tells you which version of the app the user originally downloaded. If that version was a paid version, you know the user already paid.</p><pre><code class="language-swift">import StoreKit

func shouldGrantFullAccess() async -&gt; Bool {
   do {
      let result = try await AppTransaction.shared
      if case .verified(let transaction) = result {
         let originalVersion = transaction.originalAppVersion
         // Version &quot;2.0&quot; was when the app went freemium
         if originalVersion.compare(&quot;2.0&quot;, options: .numeric) == .orderedAscending {
            return true // User purchased before freemium transition
         }
      }
   } catch {
      // Handle verification failure
   }
   return false
}</code></pre><p>The logic is straightforward: compare the user’s <code>originalAppVersion</code> against the version where you made the freemium switch. If their original version predates the change, grant them full access automatically.</p><h2 id="important-details">Important Details</h2><p>The <code>originalAppVersion</code> corresponds to the <code>CFBundleShortVersionString</code> value at the time of the original purchase (or download for free apps). Make sure you know exactly which version number marks your transition point.</p><p>This approach requires no server infrastructure and no migration code. StoreKit handles verification through the App Store receipt chain, so the check is tamper-resistant. Apple documents this pattern in their <a href="https://developer.apple.com/documentation/storekit/apptransaction/3954437-originalappversion">original API for business model migration</a>.</p><p>For TestFlight and simulator testing, <code>originalAppVersion</code> returns the <code>CFBundleVersion</code> (build number) instead, so plan your test cases accordingly.</p>]]></content:encoded>
</item>
<item>
<title>Xcode Quick Help in the Sidebar</title>
<link>https://fline.dev/snippets/xcode-quick-help-sidebar/</link>
<guid isPermaLink="true">https://fline.dev/snippets/xcode-quick-help-sidebar/</guid>
<pubDate>Mon, 25 Mar 2024 00:00:00 +0000</pubDate>
<description><![CDATA[The Quick Help inspector in Xcode's sidebar auto-updates documentation as your cursor moves, removing the need to Cmd-click for docs.]]></description>
<content:encoded><![CDATA[<h2 id="an-overlooked-feature">An Overlooked Feature</h2><p>For years, my workflow for checking documentation in Xcode was the same: Cmd-click a symbol, select “Show Quick Help” from the context menu, read the popup, then dismiss it and continue coding. It works, but it is interruptive – each lookup requires three actions and breaks your editing flow.</p><p>Then I accidentally discovered the Quick Help inspector panel in the right sidebar.</p><p><video src="/assets/images/snippets/xcode-quick-help-sidebar/demo.mp4" controls muted playsinline></video></p><h2 id="how-it-works">How It Works</h2><p>Open the Quick Help inspector with Option+Cmd+3, or go to View &gt; Inspectors &gt; Quick Help. This opens a panel in the right sidebar that displays documentation for whatever symbol your cursor is currently on. As you move your cursor through your code – clicking on a method, arrowing through parameters, selecting a type – the sidebar updates automatically.</p><p>There is no clicking, no popup to dismiss, no interruption. You just keep coding and the relevant documentation follows along.</p><h2 id="why-it-beats-cmd-click">Why It Beats Cmd-Click</h2><p>The Cmd-click approach has a specific cost: it requires you to decide “I need docs for this” before you look. The sidebar inverts that. Because it is always visible, you absorb documentation passively. You notice parameter descriptions, return types, deprecation warnings, and availability annotations without making a conscious effort to look them up.</p><p>This is particularly valuable when working with unfamiliar APIs. Instead of Cmd-clicking every other symbol, you simply move your cursor through the code and read the sidebar as you go. It turns documentation lookup from a discrete action into a continuous stream.</p><p>The panel shows the same content as the Quick Help popup: declaration, description, parameters, return value, availability, and related symbols. The only difference is that it persists and updates in place rather than appearing and disappearing.</p><p>If you have the screen space for the right sidebar, keeping Quick Help open while coding is one of those small workflow changes that compounds over time.</p>]]></content:encoded>
</item>
<item>
<title>Building an AsyncButton in SwiftUI</title>
<link>https://fline.dev/snippets/asyncbutton-swiftui-progress-status/</link>
<guid isPermaLink="true">https://fline.dev/snippets/asyncbutton-swiftui-progress-status/</guid>
<pubDate>Wed, 27 Sep 2023 00:00:00 +0000</pubDate>
<description><![CDATA[A reusable button component that handles async actions with automatic loading state, disabling, and success/failure indication.]]></description>
<content:encoded><![CDATA[<h2 id="the-need-for-asyncbutton">The Need for AsyncButton</h2><p>Standard SwiftUI <code>Button</code> actions are synchronous. When you need to perform an async operation – a network request, a database write, a StoreKit purchase – you end up manually managing a <code>Task</code>, tracking loading state, disabling the button, and handling errors. This boilerplate repeats across every async button in your app.</p><p>I built an <code>AsyncButton</code> component that wraps all of this into a single reusable view.</p><p><video src="/assets/images/snippets/asyncbutton-swiftui-progress-status/demo.mp4" controls muted playsinline></video></p><h2 id="a-simplified-implementation">A Simplified Implementation</h2><p>Here is the core idea:</p><pre><code class="language-swift">struct AsyncButton&lt;Label: View&gt;: View {
   let action: () async throws -&gt; Void
   let label: () -&gt; Label

   @State private var isRunning = false
   @State private var result: Result&lt;Void, Error&gt;?

   var body: some View {
      Button {
         isRunning = true
         Task {
            do {
               try await action()
               result = .success(())
            } catch {
               result = .failure(error)
            }
            isRunning = false
         }
      } label: {
         HStack(spacing: 8) {
            label()
            if isRunning {
               ProgressView()
            }
         }
      }
      .disabled(isRunning)
   }
}</code></pre><p>The button creates a <code>Task</code> internally, so callers can use <code>await</code> directly in the action closure. While the task runs, a <code>ProgressView</code> appears next to the label and the button is disabled to prevent duplicate submissions. The <code>result</code> state can drive success or failure indicators – a checkmark, a color flash, or a shake animation.</p><h2 id="usage">Usage</h2><p>Using it feels natural:</p><pre><code class="language-swift">AsyncButton {
   try await viewModel.submitOrder()
} label: {
   Text(&quot;Place Order&quot;)
}</code></pre><p>No manual state management, no <code>Task</code> creation at the call site. The full implementation in <a href="https://github.com/FlineDev/HandySwiftUI">HandySwiftUI</a> adds configurable success/failure animations, customizable progress indicators, and support for button styles. But the core pattern above covers the most common case and is straightforward to adapt to your own projects.</p>]]></content:encoded>
</item>
<item>
<title>ImageRenderer Cannot Export UIKit-Backed Views</title>
<link>https://fline.dev/snippets/imagerenderer-uikit-backed-views/</link>
<guid isPermaLink="true">https://fline.dev/snippets/imagerenderer-uikit-backed-views/</guid>
<pubDate>Tue, 19 Sep 2023 00:00:00 +0000</pubDate>
<description><![CDATA[SwiftUI's ImageRenderer silently fails on views that use UIKit or AppKit under the hood, like List and ScrollView.]]></description>
<content:encoded><![CDATA[<h2 id="the-limitation">The Limitation</h2><p>SwiftUI’s <code>ImageRenderer</code> lets you render any SwiftUI view into a <code>UIImage</code> or <code>CGImage</code>. It works well for pure SwiftUI views, but silently fails – producing a blank or incomplete image – when the view tree contains components backed by UIKit or AppKit. This includes several commonly used views:</p><ul><li><p><code>List</code> (wraps UITableView / NSTableView)</p></li><li><p><code>ScrollView</code> (wraps UIScrollView / NSScrollView)</p></li><li><p><code>TextEditor</code> (wraps UITextView / NSTextView)</p></li><li><p><code>Map</code> (wraps MKMapView)</p></li></ul><p>There is no compiler warning or runtime error. The renderer simply does not capture those portions of the view hierarchy.</p><h2 id="the-workaround">The Workaround</h2><p>When I needed to export a list-like layout as an image, the solution was to replace <code>List</code> with a pure SwiftUI equivalent built from <code>VStack</code> and manual styling:</p><pre><code class="language-swift">let exportView = VStack(spacing: 0) {
   ForEach(items) { item in
      HStack {
         Text(item.name)
         Spacer()
         Text(item.value)
            .foregroundStyle(.secondary)
      }
      .padding(.horizontal, 16)
      .padding(.vertical, 12)

      if item.id != items.last?.id {
         Divider()
      }
   }
}
.background(.white)
.frame(width: 390)

let renderer = ImageRenderer(content: exportView)
renderer.scale = UIScreen.main.scale

if let image = renderer.uiImage {
   // Use the rendered image
}</code></pre><p>The <code>VStack</code> with <code>ForEach</code> replicates the visual structure of a <code>List</code> without relying on any UIKit-backed views. Adding dividers, padding, and a background produces a result that looks close enough to a standard list for export purposes.</p><h2 id="practical-advice">Practical Advice</h2><p>If you plan to use <code>ImageRenderer</code> in your app, design your exportable views with this constraint in mind from the start. Build them from basic SwiftUI primitives: stacks, text, shapes, and images. Avoid any view that you know wraps a platform-specific control. Testing the render output early saves the frustration of discovering blank regions later.</p>]]></content:encoded>
</item>
<item>
<title>Combine Swift Imports with a Wrapper Module</title>
<link>https://fline.dev/snippets/combine-swift-imports-wrapper-module/</link>
<guid isPermaLink="true">https://fline.dev/snippets/combine-swift-imports-wrapper-module/</guid>
<pubDate>Wed, 13 Sep 2023 00:00:00 +0000</pubDate>
<description><![CDATA[Create a single import that re-exports all your commonly used frameworks using @_exported import.]]></description>
<content:encoded><![CDATA[<h2 id="the-repetitive-import-problem">The Repetitive Import Problem</h2><p>In any Swift project of moderate size, you end up with the same set of imports at the top of nearly every file. Foundation, SwiftUI, OSLog, maybe Observation – the list grows as you adopt new frameworks. With the move to structured logging via OSLog, I found myself adding <code>import OSLog</code> to almost every file alongside the usual suspects.</p><p>The solution is a wrapper module that re-exports everything you commonly need through a single import.</p><h2 id="setting-up-the-wrapper-module">Setting Up the Wrapper Module</h2><p>Create a new target in your Swift package or Xcode project. In my case, I called it <code>AppFoundation</code>. The entire module consists of a single file:</p><pre><code class="language-swift">// Sources/AppFoundation/Exports.swift
@_exported import Foundation
@_exported import SwiftUI
@_exported import OSLog
@_exported import Observation</code></pre><p>The <code>@_exported</code> attribute makes all public symbols from each framework available to any file that imports <code>AppFoundation</code>. Now, every file in your app just needs:</p><pre><code class="language-swift">import AppFoundation</code></pre><p>In your <code>Package.swift</code>, the target has no source code of its own beyond the exports file. Your app target declares a dependency on <code>AppFoundation</code>, and all the re-exported frameworks become available everywhere.</p><h2 id="trade-offs-to-consider">Trade-Offs to Consider</h2><p>This approach has clear benefits: less boilerplate, fewer merge conflicts in import sections, and a single place to add new framework imports. But there are trade-offs.</p><p>First, <code>@_exported</code> is an underscored attribute, meaning it is not officially part of the stable Swift API. In practice, it has been stable for years and is used widely in the ecosystem, but it carries no formal guarantee.</p><p>Second, implicit dependencies can make it harder to understand what a file actually uses. When every file has access to everything, you lose the documentation value of explicit imports. If you later extract a module into a standalone package, you will need to add the explicit imports back.</p><p>For app targets where convenience matters more than strict modularity, the wrapper module pattern saves real time. For reusable libraries, explicit imports remain the better choice.</p>]]></content:encoded>
</item>
<item>
<title>SwiftUI Navigation: Present Data, Not Views</title>
<link>https://fline.dev/snippets/swiftui-navigation-present-data/</link>
<guid isPermaLink="true">https://fline.dev/snippets/swiftui-navigation-present-data/</guid>
<pubDate>Thu, 27 Jul 2023 00:00:00 +0000</pubDate>
<description><![CDATA[Understanding the mental model shift from imperative navigation in UIKit to data-driven navigation in SwiftUI.]]></description>
<content:encoded><![CDATA[<h2 id="the-mental-model-shift">The Mental Model Shift</h2><p>In UIKit, navigation is imperative. You tell the system exactly what to do:</p><pre><code class="language-swift">let detailVC = DetailViewController()
detailVC.item = selectedItem
navigationController?.pushViewController(detailVC, animated: true)</code></pre><p>You create a view controller, configure it, and push it onto the stack. You are in control of the action.</p><p>SwiftUI works differently. You do not navigate – you present data in new views. Everything is data-driven. You do not control views. You control data.</p><h2 id="data-driven-navigation-in-practice">Data-Driven Navigation in Practice</h2><p>With <code>NavigationStack</code>, navigation is driven by state. You declare what data maps to what view, and SwiftUI handles the transitions:</p><pre><code class="language-swift">struct ContentView: View {
   @State private var path: [Item] = []

   var body: some View {
      NavigationStack(path: $path) {
         List(items) { item in
            Button(item.name) {
               path.append(item)  // Modify data, not views
            }
         }
         .navigationDestination(for: Item.self) { item in
            DetailView(item: item)
         }
      }
   }
}</code></pre><p>The key line is <code>path.append(item)</code>. You are not pushing a view. You are adding data to an array. SwiftUI observes the change and presents the corresponding view automatically.</p><h2 id="why-this-matters">Why This Matters</h2><p>This distinction is not just philosophical – it has practical consequences. Because navigation is state, you get deep linking for free by constructing the right path array. You can persist and restore navigation state by saving the path. You can programmatically navigate to any depth by appending multiple items at once.</p><p>It also means dismissal is just data removal. Calling <code>path.removeLast()</code> pops the top view. Clearing the array returns to root. No need to track view controller references or walk the navigation hierarchy.</p><p>The shift takes time to internalize, especially if you have years of UIKit experience. But once it clicks, SwiftUI navigation becomes far more predictable. Your views become pure functions of your data, and navigation is just another piece of that data.</p>]]></content:encoded>
</item>
<item>
<title>AsyncImage Does Not Support .resizable()</title>
<link>https://fline.dev/snippets/asyncimage-resizable-modifier/</link>
<guid isPermaLink="true">https://fline.dev/snippets/asyncimage-resizable-modifier/</guid>
<pubDate>Wed, 26 Jul 2023 00:00:00 +0000</pubDate>
<description><![CDATA[SwiftUI's AsyncImage does not allow the .resizable() modifier, requiring a phase-based workaround.]]></description>
<content:encoded><![CDATA[<h2 id="the-problem">The Problem</h2><p>SwiftUI’s <code>AsyncImage</code> is convenient for loading remote images, but it has a surprising limitation: you cannot apply the <code>.resizable()</code> modifier to it. This code compiles but does not behave as expected:</p><pre><code class="language-swift">// This does NOT work as intended
AsyncImage(url: imageURL)
   .resizable()  // Has no effect -- AsyncImage is not an Image
   .aspectRatio(contentMode: .fill)
   .frame(width: 200, height: 200)</code></pre><p>The reason is that <code>AsyncImage</code> is not an <code>Image</code> – it is a container view that manages loading state. The <code>.resizable()</code> modifier is defined only on <code>Image</code>, so applying it to <code>AsyncImage</code> just calls the generic <code>View</code> version, which does nothing useful.</p><h2 id="the-solution">The Solution</h2><p>The fix is to use the phase-based initializer, which gives you direct access to the underlying <code>Image</code> value once loading completes:</p><pre><code class="language-swift">AsyncImage(url: imageURL) { phase in
   switch phase {
   case .success(let image):
      image
         .resizable()
         .aspectRatio(contentMode: .fill)
   case .failure:
      Image(systemName: &quot;photo&quot;)
         .foregroundStyle(.secondary)
   case .empty:
      ProgressView()
   @unknown default:
      EmptyView()
   }
}
.frame(width: 200, height: 200)
.clipped()</code></pre><p>Inside the <code>.success</code> case, <code>image</code> is a real <code>Image</code> value, so <code>.resizable()</code> works correctly. This also gives you control over the loading and error states, which is better practice anyway.</p><h2 id="when-to-load-manually">When to Load Manually</h2><p>If you need the raw image data – for example, to cache it, inspect its size, or create a <code>UIImage</code> – you may want to skip <code>AsyncImage</code> entirely and load with <code>URLSession</code>. But for most display-only cases, the phase-based initializer covers the need without extra complexity.</p><p>This is one of those SwiftUI APIs where the simple initializer looks appealing but falls short in practice. Default to the phase-based version whenever you need any image-specific modifiers.</p>]]></content:encoded>
</item>
<item>
<title>Multi-Line Code with Ctrl+M in Xcode 15</title>
<link>https://fline.dev/snippets/multi-line-code-ctrl-m-xcode-15/</link>
<guid isPermaLink="true">https://fline.dev/snippets/multi-line-code-ctrl-m-xcode-15/</guid>
<pubDate>Tue, 25 Jul 2023 00:00:00 +0000</pubDate>
<description><![CDATA[Xcode 15 introduces a Ctrl+M shortcut to expand function calls and parameters across multiple lines.]]></description>
<content:encoded><![CDATA[<h2 id="expanding-code-to-multiple-lines">Expanding Code to Multiple Lines</h2><p>Xcode 15 introduced a small but impactful editing shortcut: Ctrl+M. Place your cursor on a function call, initializer, or any comma-separated parameter list, press Ctrl+M, and Xcode automatically expands it across multiple lines – one parameter per line, properly indented.</p><p><img src="/assets/images/snippets/multi-line-code-ctrl-m-xcode-15/ctrl-m-shortcut.webp" alt="Ctrl+M shortcut" loading="lazy" /></p><p>Before this shortcut, reformatting a long function call meant manually adding line breaks and fixing indentation. Consider a call like this:</p><pre><code class="language-swift">let label = UILabel(frame: .zero, font: .systemFont(ofSize: 14), textColor: .label, numberOfLines: 0)</code></pre><p>After pressing Ctrl+M with the cursor on that line, Xcode reformats it to:</p><pre><code class="language-swift">let label = UILabel(
   frame: .zero,
   font: .systemFont(ofSize: 14),
   textColor: .label,
   numberOfLines: 0
)</code></pre><h2 id="when-to-use-it">When to Use It</h2><p>This shortcut is most valuable when you are writing SwiftUI view modifiers or initializers that accumulate parameters over time. A view that starts with two parameters often grows to five or six as you add configuration. Rather than reformatting manually each time, Ctrl+M handles it in one keystroke.</p><p>It also works in reverse – if your parameters are already on separate lines, pressing Ctrl+M collapses them back into a single line. This toggle behavior makes it easy to switch between compact and expanded formats depending on readability needs.</p><p>One thing to note: the shortcut operates on the innermost parameter list at the cursor position. If you have nested function calls, position your cursor carefully to expand the right one.</p>]]></content:encoded>
</item>
<item>
<title>Search Apple Documentation with Shift+Cmd+O</title>
<link>https://fline.dev/snippets/search-apple-documentation-shift-cmd-o/</link>
<guid isPermaLink="true">https://fline.dev/snippets/search-apple-documentation-shift-cmd-o/</guid>
<pubDate>Sat, 10 Jun 2023 00:00:00 +0000</pubDate>
<description><![CDATA[Use the same Open Quickly shortcut to search Apple's developer documentation directly on the web.]]></description>
<content:encoded><![CDATA[<h2 id="open-quickly-for-documentation">Open Quickly for Documentation</h2><p>Most Xcode users are familiar with Shift+Cmd+O – the “Open Quickly” shortcut that lets you jump to any file, symbol, or type in your project. It is one of those shortcuts that becomes second nature within days of using Xcode. What I did not realize until recently is that the same shortcut now works on the Apple Developer documentation website, powered by DocC.</p><p><img src="/assets/images/snippets/search-apple-documentation-shift-cmd-o/search-documentation.webp" alt="Search documentation" loading="lazy" /></p><p>When browsing <a href="https://developer.apple.com/documentation">developer.apple.com/documentation</a>, pressing Shift+Cmd+O opens a search overlay that behaves just like Xcode’s Open Quickly dialog. You can type a framework name, a class, a method, or even a partial match, and results appear instantly. Selecting a result navigates directly to that documentation page.</p><h2 id="why-this-matters">Why This Matters</h2><p>Before this feature, searching Apple’s documentation meant scrolling through the sidebar hierarchy or using the general search bar, which often returned a mix of articles, tutorials, and API references. The Open Quickly overlay filters results to API symbols and pages, making it far more precise.</p><p>This is especially useful when you are in a browser reading a WWDC article or forum thread and need to quickly check the signature of a related API. Instead of switching back to Xcode, you stay in context and look it up directly.</p><p>The feature is part of Apple’s broader investment in DocC as a documentation platform. Since DocC powers both Xcode’s documentation viewer and the web-based documentation, it makes sense that the same interaction patterns carry over. If you spend time reading Apple docs in the browser, building this shortcut into your muscle memory is worth the effort.</p>]]></content:encoded>
</item>
<item>
<title>Xcode 15 Brings Type-Safe Asset Catalog Access</title>
<link>https://fline.dev/snippets/xcode-15-type-safe-asset-catalogs/</link>
<guid isPermaLink="true">https://fline.dev/snippets/xcode-15-type-safe-asset-catalogs/</guid>
<pubDate>Tue, 06 Jun 2023 00:00:00 +0000</pubDate>
<description><![CDATA[Xcode 15 generates type-safe Swift accessors for images and colors in asset catalogs, replacing the need for SwiftGen.]]></description>
<content:encoded><![CDATA[<h2 id="the-end-of-string-based-asset-references">The End of String-Based Asset References</h2><p>One of the quieter but impactful changes in Xcode 15 is built-in type-safe access to asset catalogs. Previously, referencing an image or color from your asset catalog required a string literal:</p><pre><code class="language-swift">// Before Xcode 15
Image(&quot;custom-header-icon&quot;)
Color(&quot;primaryBrand&quot;)</code></pre><p>This was fragile. Rename an asset and your code compiles fine but crashes or shows nothing at runtime. Tools like SwiftGen existed specifically to solve this by generating type-safe constants from your asset catalogs.</p><h2 id="what-changed">What Changed</h2><p>Xcode 15 now generates Swift accessors for every image and color in your asset catalog automatically. You access them through the resource initializers:</p><pre><code class="language-swift">// Xcode 15+
Image(.customHeaderIcon)
Color(.primaryBrand)</code></pre><p><img src="/assets/images/snippets/xcode-15-type-safe-asset-catalogs/asset-catalog-autocomplete.webp" alt="Xcode showing autocomplete suggestions for asset catalog resources" loading="lazy" /></p><p>The compiler knows about your assets. You get full autocomplete, and if you delete or rename an asset, you get a compile-time error instead of a silent runtime failure.</p><h2 id="apple-sherlocked-swiftgen">Apple Sherlocked SwiftGen</h2><p>This is effectively Apple integrating what SwiftGen has provided for years. For teams already using SwiftGen, this is a good time to evaluate whether you still need it. The built-in solution covers the two most common use cases – images and colors – without any build phase scripts or code generation steps.</p><p>There are still reasons to keep SwiftGen if you use it for fonts, localized strings, or other resource types. But for asset catalogs specifically, the native solution is now good enough for most projects, and it works out of the box with zero configuration.</p>]]></content:encoded>
</item>
<item>
<title>Xcode 15 String Catalogs Replace .strings and .stringsdict</title>
<link>https://fline.dev/snippets/xcode-15-string-catalogs/</link>
<guid isPermaLink="true">https://fline.dev/snippets/xcode-15-string-catalogs/</guid>
<pubDate>Tue, 06 Jun 2023 00:00:00 +0000</pubDate>
<description><![CDATA[Xcode 15 introduces String Catalogs, a visual editor for managing localized strings that replaces legacy .strings and .stringsdict files.]]></description>
<content:encoded><![CDATA[<h2 id="the-legacy-localization-workflow">The Legacy Localization Workflow</h2><p>Working with <code>.strings</code> and <code>.stringsdict</code> files has always been one of the rougher edges of Apple development. Plain text key-value files are easy to get wrong – missing semicolons, mismatched keys across languages, no way to see translation progress at a glance. And <code>.stringsdict</code> files, used for pluralization rules, are XML-based plist files that are notoriously difficult to author by hand.</p><h2 id="what-string-catalogs-bring">What String Catalogs Bring</h2><p>Xcode 15 introduces a new <code>.xcstrings</code> file format called String Catalogs. It replaces both <code>.strings</code> and <code>.stringsdict</code> with a single file that comes with a dedicated visual editor.</p><p><img src="/assets/images/snippets/xcode-15-string-catalogs/string-catalog-editor.webp" alt="The String Catalog editor in Xcode 15 showing translations across multiple languages" loading="lazy" /></p><p>The key improvements are substantial:</p><p><strong>Visual editor</strong> – All localizable strings are displayed in a table with columns for each language. You can see and edit translations inline without switching between files or using external tools.</p><p><strong>Translation progress tracking</strong> – Each language shows a completion percentage. At a glance, you can tell which languages need attention and which strings are still untranslated.</p><p><strong>Automatic string extraction</strong> – Xcode scans your Swift code and automatically discovers strings that need localization. New strings appear in the catalog without any manual registration step.</p><p><strong>Built-in migration</strong> – There is a migration path from existing <code>.strings</code> and <code>.stringsdict</code> files. Right-click your existing localization files and Xcode offers to convert them to the new format.</p><h2 id="practical-impact">Practical Impact</h2><p>For projects with even a handful of supported languages, this is a significant quality-of-life improvement. The visual editor alone eliminates an entire class of formatting errors. Combined with automatic extraction, it means fewer forgotten strings and a clearer picture of your localization status across the entire project.</p><p>If you are starting a new project on Xcode 15, String Catalogs are the default. For existing projects, the migration is straightforward and well worth doing.</p>]]></content:encoded>
</item>
<item>
<title>Xcode 15&apos;s Format to Multiple Lines Feature</title>
<link>https://fline.dev/snippets/xcode-15-format-parameters-multiple-lines/</link>
<guid isPermaLink="true">https://fline.dev/snippets/xcode-15-format-parameters-multiple-lines/</guid>
<pubDate>Tue, 06 Jun 2023 00:00:00 +0000</pubDate>
<description><![CDATA[Xcode 15 adds a built-in action to reformat long function parameter lists from a single line to multiple lines.]]></description>
<content:encoded><![CDATA[<h2 id="the-formatting-problem">The Formatting Problem</h2><p>Long function calls and declarations with many parameters are one of the most common readability issues in Swift code. You end up with lines that stretch well past any reasonable column limit:</p><pre><code class="language-swift">func configureView(title: String, subtitle: String, icon: Image, backgroundColor: Color, isEnabled: Bool, action: @escaping () -&gt; Void) {</code></pre><p>Manually breaking this into multiple lines is tedious. You have to position the cursor, add line breaks, indent each parameter, and make sure the trailing parenthesis lines up correctly.</p><h2 id="the-new-action-in-xcode-15">The New Action in Xcode 15</h2><p>Xcode 15 introduces a “Format to Multiple Lines” action that does this automatically. Place your cursor on a function call or declaration with multiple parameters, and Xcode offers to reformat it:</p><p><img src="/assets/images/snippets/xcode-15-format-parameters-multiple-lines/format-to-multiple-lines.webp" alt="The Format to Multiple Lines option in Xcode 15" loading="lazy" /></p><p>The result is cleanly formatted with one parameter per line:</p><pre><code class="language-swift">func configureView(
   title: String,
   subtitle: String,
   icon: Image,
   backgroundColor: Color,
   isEnabled: Bool,
   action: @escaping () -&gt; Void
) {</code></pre><p>You can find this action by right-clicking on the function signature and looking under <strong>Refactor</strong>, or by using the <strong>Editor</strong> menu. It works on both function declarations and call sites.</p><h2 id="when-to-use-it">When to Use It</h2><p>This is most useful right after writing a new function or adding parameters to an existing one. Instead of manually formatting as you go, you can write everything on one line and then apply the formatter in a single action. It also helps when reviewing code where someone else left long single-line signatures – select and reformat without manual editing.</p><p>The reverse operation (collapsing multiple lines back to one) is not currently available, but that direction is less commonly needed.</p>]]></content:encoded>
</item>
<item>
<title>Previewing Loading States in SwiftUI Without Changing Production Code</title>
<link>https://fline.dev/snippets/preview-loading-states-swiftui/</link>
<guid isPermaLink="true">https://fline.dev/snippets/preview-loading-states-swiftui/</guid>
<pubDate>Wed, 31 May 2023 00:00:00 +0000</pubDate>
<description><![CDATA[A preview-only helper that simulates network delays so you can see loading states in SwiftUI previews.]]></description>
<content:encoded><![CDATA[<h2 id="the-challenge">The Challenge</h2><p>When building views that depend on asynchronous data, you typically have a loading state that shows a spinner or placeholder. In production, this state appears briefly while data loads from the network or database. But in SwiftUI previews, your mock data is available instantly, so the loading state flashes by too fast to inspect – or never appears at all.</p><h2 id="a-preview-only-delay-helper">A Preview-Only Delay Helper</h2><p>The solution is a small helper that introduces an artificial delay, but only in preview or debug contexts. Here is the pattern:</p><pre><code class="language-swift">struct DelayedStatePreview&lt;Content: View&gt;: View {
   @State private var isLoaded = false
   let delay: Duration
   let content: (Bool) -&gt; Content

   init(
      delay: Duration = .seconds(2),
      @ViewBuilder content: @escaping (Bool) -&gt; Content
   ) {
      self.delay = delay
      self.content = content
   }

   var body: some View {
      content(isLoaded)
         .task {
            try? await Task.sleep(for: delay)
            isLoaded = true
         }
   }
}</code></pre><p>You use it in a preview like this:</p><pre><code class="language-swift">#Preview {
   DelayedStatePreview { isLoaded in
      if isLoaded {
         ArticleListView(articles: mockArticles)
      } else {
         LoadingView()
      }
   }
}</code></pre><p><img src="/assets/images/snippets/preview-loading-states-swiftui/code-example.webp" alt="Code example showing the preview helper in context" loading="lazy" /></p><h2 id="why-this-matters">Why This Matters</h2><p>The key benefit is that your production code stays clean. You are not adding artificial delays or debug flags to your actual views. The helper exists purely at the preview layer, giving you a way to visually verify that your loading states look correct, that transitions animate smoothly, and that layout does not jump when data arrives.</p><p>This is especially useful for views with skeleton loaders or shimmer effects where the visual quality of the loading state is part of the user experience.</p>]]></content:encoded>
</item>
<item>
<title>Speed Up Xcode Launches by Disabling Debug Executable</title>
<link>https://fline.dev/snippets/speed-up-xcode-disable-debug-executable/</link>
<guid isPermaLink="true">https://fline.dev/snippets/speed-up-xcode-disable-debug-executable/</guid>
<pubDate>Wed, 10 May 2023 00:00:00 +0000</pubDate>
<description><![CDATA[A hidden Xcode scheme setting that can significantly reduce app launch times during development.]]></description>
<content:encoded><![CDATA[<h2 id="the-blank-screen-problem">The Blank Screen Problem</h2><p>If you have ever noticed a delay – sometimes several seconds – between pressing Run in Xcode and your app actually appearing, the culprit is often the LLDB debugger attaching to your process. During this time, the Simulator shows a blank screen while the debugger initializes.</p><p><img src="/assets/images/snippets/speed-up-xcode-disable-debug-executable/comparison.webp" alt="Comparison of launch times with and without debug executable enabled" loading="lazy" /></p><h2 id="where-to-find-the-setting">Where to Find the Setting</h2><p>The setting lives in your scheme configuration:</p><ol><li><p>Go to <strong>Product &gt; Scheme &gt; Edit Scheme</strong> (or press Cmd+Shift+&lt;)</p></li><li><p>Select the <strong>Run</strong> action on the left</p></li><li><p>Switch to the <strong>Info</strong> tab</p></li><li><p>Uncheck <strong>Debug executable</strong></p></li></ol><p><img src="/assets/images/snippets/speed-up-xcode-disable-debug-executable/setting-before.webp" alt="The setting before the change" loading="lazy" /></p><p><img src="/assets/images/snippets/speed-up-xcode-disable-debug-executable/setting-after.webp" alt="The setting after unchecking Debug executable" loading="lazy" /></p><h2 id="what-this-does">What This Does</h2><p>When “Debug executable” is enabled (the default), Xcode attaches the LLDB debugger to your app process at launch. This is what enables breakpoints, the debug memory graph, view hierarchy debugger, and <code>po</code> commands in the console.</p><p>Disabling it skips the debugger attachment entirely. Your app launches noticeably faster – in my experience, the difference can be 2 to 5 seconds on larger projects. Console output via <code>print()</code> and <code>os_log</code> still works normally, so you can still use logging for debugging.</p><h2 id="the-trade-off">The Trade-Off</h2><p>Without the debugger attached, you lose:</p><ul><li><p>Breakpoints (they will not trigger)</p></li><li><p><code>po</code> and <code>expression</code> commands in the console</p></li><li><p>Memory graph and view hierarchy debugging tools</p></li></ul><p>This makes the setting ideal for UI iteration work, where you are making visual tweaks and re-running frequently. When you need to investigate a specific bug with breakpoints, just re-enable the checkbox temporarily. I keep it off most of the time and only toggle it on when I need to step through code.</p>]]></content:encoded>
</item>
<item>
<title>Xcode Code Snippets for Developer Warnings</title>
<link>https://fline.dev/snippets/xcode-snippets-developer-warnings/</link>
<guid isPermaLink="true">https://fline.dev/snippets/xcode-snippets-developer-warnings/</guid>
<pubDate>Sun, 07 May 2023 00:00:00 +0000</pubDate>
<description><![CDATA[Using Xcode code snippets with #warning to leave actionable reminders in your codebase.]]></description>
<content:encoded><![CDATA[<h2 id="why-warning-matters-during-development">Why #warning Matters During Development</h2><p>Swift’s <code>#warning</code> directive generates a compiler warning with a custom message. Unlike comments, these show up in the Issue Navigator and in the build output, making them impossible to miss. I use them as hard reminders for things that must be addressed before shipping.</p><h2 id="two-snippets-i-use-daily">Two Snippets I Use Daily</h2><p>I have two Xcode code snippets configured for this purpose.</p><p><strong>“nyi” – Not Yet Implemented:</strong></p><pre><code class="language-swift">#warning(&quot;Not yet implemented!&quot;)</code></pre><p>I type <code>nyi</code> and hit Enter whenever I stub out a function or skip a code path during prototyping. It compiles fine but the warning ensures I come back to finish the work.</p><p><strong>“dw” – Developer Warning:</strong></p><pre><code class="language-swift">#warning(&quot;&lt;#message#&gt;&quot;)</code></pre><p>This one uses a placeholder token so that after typing <code>dw</code> and pressing Enter, the cursor lands inside the message and I can type a custom note. I use this for things like <code>#warning(&quot;Handle error case for offline mode&quot;)</code> or <code>#warning(&quot;Remove before release&quot;)</code>.</p><h2 id="how-to-create-xcode-snippets">How to Create Xcode Snippets</h2><p>Setting these up takes about 30 seconds each:</p><ol><li><p>Type the code you want as a snippet in any Swift file</p></li><li><p>Select the code</p></li><li><p>Right-click and choose <strong>Create Code Snippet</strong></p></li><li><p>Give it a title (e.g., “Developer Warning”)</p></li><li><p>Set the <strong>Completion</strong> shortcut (e.g., <code>dw</code>)</p></li><li><p>Set <strong>Availability</strong> to “All” or “Swift” scopes</p></li><li><p>Click Done</p></li></ol><p>From that point on, typing the shortcut in any Swift file offers the snippet via autocomplete.</p><p>The key advantage over plain comments is visibility. A <code>// TODO:</code> comment is easy to ignore and hard to search for consistently. A <code>#warning</code> forces the compiler to surface it every single build, keeping your unfinished work front and center until you resolve it.</p>]]></content:encoded>
</item>
<item>
<title>Quick Access to Swift Evolution Proposal Summaries on GitHub</title>
<link>https://fline.dev/snippets/swift-evolution-proposal-summaries-github/</link>
<guid isPermaLink="true">https://fline.dev/snippets/swift-evolution-proposal-summaries-github/</guid>
<pubDate>Tue, 02 May 2023 00:00:00 +0000</pubDate>
<description><![CDATA[A simple URL trick to read summarized versions of Swift Evolution proposals on GitHub.]]></description>
<content:encoded><![CDATA[<h2 id="the-problem-with-proposal-documents">The Problem with Proposal Documents</h2><p>Swift Evolution proposals are thorough by design. They cover motivation, detailed design, alternatives considered, ABI implications, and more. That thoroughness is essential for the review process, but when you just want to understand what a proposal does and why, reading through thousands of words can be a lot.</p><h2 id="the-url-trick">The URL Trick</h2><p>There is a simple way to get a summarized version of any Swift Evolution proposal on GitHub. When you are viewing a proposal at a URL like:</p><pre><code>https://github.com/apple/swift-evolution/blob/main/proposals/0390-noncopyable-structs-and-enums.md</code></pre><p>Replace <code>apple</code> with <code>FlineDev</code>:</p><pre><code>https://github.com/FlineDev/swift-evolution/blob/main/proposals/0390-noncopyable-structs-and-enums.md</code></pre><p>That takes you to a fork of the swift-evolution repository where proposals have been augmented with AI-generated summaries at the top. Each summary distills the key points – what the proposal introduces, why it matters, and the basic syntax – into a few paragraphs.</p><h2 id="when-this-helps">When This Helps</h2><p>This is particularly useful when you see a proposal mentioned in release notes or on social media and want to quickly understand the gist. Instead of spending 15 minutes reading the full proposal, you get the essential information in a couple of minutes.</p><p>The summaries cover most recently accepted proposals. For older proposals that predate the fork, you will still see the original text. But for anything from the last few years of Swift Evolution, the summarized version is a real time-saver.</p><p>I built this fork because I found myself repeatedly skimming proposals for just the core idea, and figured other developers might benefit from the same shortcut.</p>]]></content:encoded>
</item>
<item>
<title>Pulsating Button Animation in SwiftUI</title>
<link>https://fline.dev/snippets/pulsating-button-animation-swiftui/</link>
<guid isPermaLink="true">https://fline.dev/snippets/pulsating-button-animation-swiftui/</guid>
<pubDate>Fri, 07 Apr 2023 00:00:00 +0000</pubDate>
<description><![CDATA[How to create a pulsating button effect in SwiftUI to guide users during onboarding.]]></description>
<content:encoded><![CDATA[<h2 id="guiding-users-with-a-pulsating-button">Guiding Users with a Pulsating Button</h2><p>When building an onboarding flow, one challenge is directing the user’s attention to the next action they should take. A subtle pulsating animation on buttons draws the eye without being intrusive. I implemented this in SwiftUI using a combination of <code>scaleEffect</code> and <code>opacity</code> modifiers tied to a repeating animation.</p><p>Here is the core approach:</p><pre><code class="language-swift">struct PulsatingButtonStyle: ButtonStyle {
   @State private var isPulsating = false

   func makeBody(configuration: Configuration) -&gt; some View {
      configuration.label
         .scaleEffect(isPulsating ? 1.06 : 1.0)
         .opacity(isPulsating ? 0.8 : 1.0)
         .animation(
            .easeInOut(duration: 0.8)
               .repeatForever(autoreverses: true),
            value: isPulsating
         )
         .onAppear {
            isPulsating = true
         }
   }
}</code></pre><p>The trick is combining two animations – scale and opacity – that run in sync. The <code>repeatForever(autoreverses: true)</code> modifier makes the animation bounce back and forth continuously. By toggling <code>isPulsating</code> on appear, the animation starts immediately when the view is displayed.</p><p><video src="/assets/images/snippets/pulsating-button-animation-swiftui/demo.mp4" controls muted playsinline></video></p><p>The scale factor of 1.06 is intentionally subtle. Going much higher (like 1.2) makes the animation feel aggressive and distracting. The slight opacity shift adds depth to the pulse without making the button hard to read.</p><p>This pattern works well for onboarding screens where you want to highlight a “Continue” or “Get Started” button. Once the user has moved past onboarding, the animation is no longer shown, so it does not become annoying over time.</p><p>You can apply it to any button with <code>.buttonStyle(PulsatingButtonStyle())</code>, keeping the animation logic cleanly separated from your button content.</p>]]></content:encoded>
</item>
</channel>
</rss>