<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Technical Details on RawCull</title><link>https://rawcull.netlify.app/categories/technical-details/</link><description>Recent content in Technical Details on RawCull</description><generator>Hugo</generator><language>en</language><lastBuildDate>Thu, 09 Apr 2026 07:16:51 +0200</lastBuildDate><atom:link href="https://rawcull.netlify.app/categories/technical-details/index.xml" rel="self" type="application/rss+xml"/><item><title>Swift Concurrency in RawCull</title><link>https://rawcull.netlify.app/blog/2026/03/26/swift-concurrency-in-rawcull/</link><pubDate>Thu, 26 Mar 2026 00:00:00 +0000</pubDate><guid>https://rawcull.netlify.app/blog/2026/03/26/swift-concurrency-in-rawcull/</guid><description>&lt;h1 id="swift-concurrency-in-rawcull"&gt;Swift Concurrency in RawCull&lt;a class="td-heading-self-link" href="#swift-concurrency-in-rawcull" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;A summarized document about Concurrency in RawCull.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1--why-concurrency-matters-in-rawcull"&gt;1 Why Concurrency Matters in RawCull&lt;a class="td-heading-self-link" href="#1--why-concurrency-matters-in-rawcull" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;RawCull is a macOS photo-culling application that works with Sony A1 ARW raw files. A single RAW file from the A1 can be 50–80 MB. When you open a folder with hundreds of shots, the app must scan metadata, extract embedded JPEG previews, decode thumbnails, and manage a multi-gigabyte in-memory cache — all while keeping the UI perfectly fluid and responsive at 60 fps. Without concurrency that would be impossible.&lt;/p&gt;</description></item><item><title>Sharpness Scoring</title><link>https://rawcull.netlify.app/blog/2026/03/25/sharpness-scoring/</link><pubDate>Wed, 25 Mar 2026 00:00:00 +0000</pubDate><guid>https://rawcull.netlify.app/blog/2026/03/25/sharpness-scoring/</guid><description>&lt;h1 id="updated-sharpness-scoring-in-rawcull"&gt;Updated Sharpness Scoring in RawCull&lt;a class="td-heading-self-link" href="#updated-sharpness-scoring-in-rawcull" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This document describes how RawCull computes sharpness scores, what each parameter does, and a boundary-value test procedure for validating parameter behaviour. This applies to version 1.3.7 of RawCull&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="how-the-scoring-pipeline-works"&gt;How the Scoring Pipeline Works&lt;a class="td-heading-self-link" href="#how-the-scoring-pipeline-works" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;pre class="mermaid"&gt;flowchart TD
 A([ARW File]) --&amp;gt; B[Thumbnail decode\nthumbnailMaxPixelSize]
 A --&amp;gt; C[Saliency detection\nApple Vision]

 B --&amp;gt; D[Gaussian pre-blur\npreBlurRadius × isoFactor × resFactor]
 D --&amp;gt; E[Laplacian Metal kernel\nfocusLaplacian]
 E --&amp;gt; F[Amplify\nenergyMultiplier]
 F --&amp;gt; G[Border suppression\nborderInsetFraction]
 G --&amp;gt; H[Render to Float32 buffer]

 H --&amp;gt; I[Full-frame samples\nborder-inset region]
 C --&amp;gt;|bounding box ≥ 3% area| J[Salient-region samples\nVision bounding box]
 H --&amp;gt; J

 I --&amp;gt; K[p95 winsorized tail score\nfull-frame f]
 J --&amp;gt; L[p95 winsorized tail score\nsubject s]

 K --&amp;gt; M{Fusion}
 L --&amp;gt; M
 C --&amp;gt;|area| M

 M --&amp;gt; N[finalScore]
 N --&amp;gt; O([Badge: score / maxScore × 100])

 style A fill:#2d2d2d,color:#fff
 style O fill:#2d2d2d,color:#fff&lt;/pre&gt;
&lt;p&gt;Each ARW file goes through the following stages in detail:&lt;/p&gt;</description></item><item><title>Memory Cache</title><link>https://rawcull.netlify.app/blog/2026/03/17/memory-cache/</link><pubDate>Tue, 17 Mar 2026 00:00:00 +0000</pubDate><guid>https://rawcull.netlify.app/blog/2026/03/17/memory-cache/</guid><description>&lt;h1 id="cache-system--rawcull"&gt;Cache System — RawCull&lt;a class="td-heading-self-link" href="#cache-system--rawcull" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;RawCull uses a three-layer cache to avoid repeated RAW decoding. Decoding an ARW file on demand is expensive — the three-layer approach ensures that most requests are served from RAM or disk rather than from source.&lt;/p&gt;
&lt;p&gt;Layers (fastest to slowest):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Memory cache&lt;/strong&gt; — &lt;code&gt;NSCache&amp;lt;NSURL, DiscardableThumbnail&amp;gt;&lt;/code&gt; in RAM&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Disk cache&lt;/strong&gt; — JPEG files on disk in &lt;code&gt;~/Library/Caches/no.blogspot.RawCull/Thumbnails/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Source decode&lt;/strong&gt; — &lt;code&gt;CGImageSourceCreateThumbnailAtIndex&lt;/code&gt; from the ARW file&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The same cache stack is shared by two paths: the bulk preload flow (&lt;code&gt;ScanAndCreateThumbnails&lt;/code&gt;) and on-demand per-file requests (&lt;code&gt;RequestThumbnail&lt;/code&gt;).&lt;/p&gt;</description></item><item><title>Thumbnails</title><link>https://rawcull.netlify.app/blog/2026/03/17/thumbnails/</link><pubDate>Tue, 17 Mar 2026 00:00:00 +0000</pubDate><guid>https://rawcull.netlify.app/blog/2026/03/17/thumbnails/</guid><description>&lt;h1 id="thumbnails-and-previews--rawcull"&gt;Thumbnails and Previews — RawCull&lt;a class="td-heading-self-link" href="#thumbnails-and-previews--rawcull" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;RawCull handles Sony ARW files through two distinct image paths:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Generated thumbnails&lt;/strong&gt; — fast, sized-down previews for browsing and culling, extracted with ImageIO and cached in RAM and on disk&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Embedded JPEG previews&lt;/strong&gt; — full-resolution embedded JPEGs extracted from the ARW binary for high-quality inspection and export&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Both paths integrate with the shared three-layer cache system: RAM → disk → source decode.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-thumbnail-sizes-and-settings"&gt;1. Thumbnail Sizes and Settings&lt;a class="td-heading-self-link" href="#1-thumbnail-sizes-and-settings" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;All thumbnail dimensions are configurable via &lt;code&gt;SettingsViewModel&lt;/code&gt; and persisted to &lt;code&gt;~/Library/Application Support/RawCull/settings.json&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Scan and Thumbnail Pipeline</title><link>https://rawcull.netlify.app/blog/2026/03/25/scan-and-thumbnail-pipeline/</link><pubDate>Wed, 25 Mar 2026 00:00:00 +0000</pubDate><guid>https://rawcull.netlify.app/blog/2026/03/25/scan-and-thumbnail-pipeline/</guid><description>&lt;h1 id="rawcull--scan-and-thumbnail-pipeline"&gt;RawCull — Scan and Thumbnail Pipeline&lt;a class="td-heading-self-link" href="#rawcull--scan-and-thumbnail-pipeline" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This document describes the complete execution flow from the moment a user opens
a catalog folder to the point where all thumbnails are visible in the grid.
It covers the actors involved, the data flow between them, the concurrency
model, five performance bugs that were found and fixed, and the measured results
on a real catalog of 809 Sony A1 ARW files stored on an external 800 MB/s SSD.&lt;/p&gt;</description></item><item><title>Sony MakerNote Parser</title><link>https://rawcull.netlify.app/blog/2026/03/25/sony-makernote-parser/</link><pubDate>Wed, 25 Mar 2026 00:00:00 +0000</pubDate><guid>https://rawcull.netlify.app/blog/2026/03/25/sony-makernote-parser/</guid><description>&lt;h1 id="sony-makernote-parser--focus-point-extraction"&gt;Sony MakerNote Parser — Focus Point Extraction&lt;a class="td-heading-self-link" href="#sony-makernote-parser--focus-point-extraction" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Files covered:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RawCull/Enum/SonyMakerNoteParser.swift&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RawCull/Model/ViewModels/FocusPointsModel.swift&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RawCull/Actors/ScanFiles.swift&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RawCull/Views/FocusPoints/FocusOverlayView.swift&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="overview"&gt;Overview&lt;a class="td-heading-self-link" href="#overview" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;RawCull extracts autofocus (AF) focus point coordinates directly from Sony ARW
raw files without requiring any external tools such as &lt;code&gt;exiftool&lt;/code&gt;. The parser
supports the ILCE-1 (A1), ILCE-1M2 (A1 II), ILCE-7M5 (A7 V), ILCE-7RM5
(A7R V), and ILCE-9M3 (A9 III).&lt;/p&gt;
&lt;p&gt;The focus location is stored inside the Sony proprietary MakerNote block
embedded in the EXIF data of every ARW file. &lt;code&gt;SonyMakerNoteParser&lt;/code&gt; (declared
as an &lt;code&gt;enum&lt;/code&gt; to prevent instantiation) navigates the binary TIFF structure to
locate and decode this data. It also exposes helpers for locating and reading
the three JPEG images embedded in every ARW file, and a verbose diagnostic
walk used by the body-compatibility test suite.&lt;/p&gt;</description></item><item><title>Concurrency model</title><link>https://rawcull.netlify.app/blog/2026/03/17/concurrency-model/</link><pubDate>Tue, 17 Mar 2026 00:00:00 +0000</pubDate><guid>https://rawcull.netlify.app/blog/2026/03/17/concurrency-model/</guid><description>&lt;h1 id="concurrency-model--rawcull"&gt;Concurrency Model — RawCull&lt;a class="td-heading-self-link" href="#concurrency-model--rawcull" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Files covered:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RawCull/Model/ViewModels/RawCullViewModel.swift&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RawCull/Views/RawCullSidebarMainView/extension+RawCullView.swift&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RawCull/Actors/ScanFiles.swift&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RawCull/Actors/ScanAndCreateThumbnails.swift&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RawCull/Actors/ExtractAndSaveJPGs.swift&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RawCull/Actors/ThumbnailLoader.swift&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RawCull/Actors/RequestThumbnail.swift&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RawCull/Actors/SharedMemoryCache.swift&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RawCull/Actors/DiskCacheManager.swift&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RawCull/Actors/SaveJPGImage.swift&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RawCull/Enum/SonyThumbnailExtractor.swift&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RawCull/Enum/JPGSonyARWExtractor.swift&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="overview"&gt;Overview&lt;a class="td-heading-self-link" href="#overview" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;RawCull uses Swift structured concurrency (&lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;, &lt;code&gt;Task&lt;/code&gt;, &lt;code&gt;TaskGroup&lt;/code&gt;, and &lt;code&gt;actor&lt;/code&gt;) across four primary flows:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Flow&lt;/th&gt;
 &lt;th&gt;Entry point&lt;/th&gt;
 &lt;th&gt;Core actor(s)&lt;/th&gt;
 &lt;th&gt;Purpose&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Catalog scan&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;RawCullViewModel.handleSourceChange(url:)&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;ScanFiles&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Scan ARW files, extract metadata, load focus points&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Thumbnail preload&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;RawCullViewModel.handleSourceChange(url:)&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;ScanAndCreateThumbnails&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Bulk-populate the thumbnail cache for a selected catalog&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;JPG extraction&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;extension+RawCullView.extractAllJPGS()&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;ExtractAndSaveJPGs&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Extract embedded JPEG previews and save to disk&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;On-demand thumbnails&lt;/td&gt;
 &lt;td&gt;UI grid + detail views&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;ThumbnailLoader&lt;/code&gt;, &lt;code&gt;RequestThumbnail&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Rate-limited, cached per-file thumbnail retrieval&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The two long-running operations (thumbnail preload and JPG extraction) share a &lt;strong&gt;two-level task pattern&lt;/strong&gt;:&lt;/p&gt;</description></item><item><title>Synchronous Code</title><link>https://rawcull.netlify.app/blog/2026/02/19/synchronous-code/</link><pubDate>Thu, 19 Feb 2026 00:00:00 +0000</pubDate><guid>https://rawcull.netlify.app/blog/2026/02/19/synchronous-code/</guid><description>&lt;h1 id="a-guide-to-handling-heavy-synchronous-code-in-swift-concurrency"&gt;A Guide to Handling Heavy Synchronous Code in Swift Concurrency&lt;a class="td-heading-self-link" href="#a-guide-to-handling-heavy-synchronous-code-in-swift-concurrency" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This post explains why CPU-intensive synchronous code (such as image decoding via ImageIO) must be dispatched off the Swift Concurrency thread pool, and shows the correct patterns RawCull uses to do so.&lt;/p&gt;
&lt;h2 id="dispatchqueueglobalqos--qos-levels-compared"&gt;&lt;code&gt;DispatchQueue.global(qos:)&lt;/code&gt; — QoS Levels Compared&lt;a class="td-heading-self-link" href="#dispatchqueueglobalqos--qos-levels-compared" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The key difference is &lt;strong&gt;priority and resource allocation&lt;/strong&gt; by the system.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="userinitiated"&gt;&lt;code&gt;.userInitiated&lt;/code&gt;&lt;a class="td-heading-self-link" href="#userinitiated" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Priority:&lt;/strong&gt; High (just below &lt;code&gt;.userInteractive&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use case:&lt;/strong&gt; Work the &lt;strong&gt;user directly triggered&lt;/strong&gt; and is actively waiting for — e.g., loading a document they tapped, parsing data to display a screen&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Expected duration:&lt;/strong&gt; Near-instantaneous to a few seconds&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;System behavior:&lt;/strong&gt; Gets &lt;strong&gt;more CPU time and higher thread priority&lt;/strong&gt; — the system treats this as urgent&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Energy impact:&lt;/strong&gt; Higher&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="utility"&gt;&lt;code&gt;.utility&lt;/code&gt;&lt;a class="td-heading-self-link" href="#utility" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Priority:&lt;/strong&gt; Low-medium&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use case:&lt;/strong&gt; Long-running work the user is &lt;strong&gt;aware of but not blocked by&lt;/strong&gt; — e.g., downloading files, importing data, periodic syncs, progress-bar tasks&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Expected duration:&lt;/strong&gt; Seconds to minutes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;System behavior:&lt;/strong&gt; Balanced CPU/energy trade-off; the system &lt;strong&gt;throttles this more aggressively&lt;/strong&gt; under load or low battery&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Energy impact:&lt;/strong&gt; Lower (system may apply energy efficiency optimizations)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="quick-comparison"&gt;Quick Comparison&lt;a class="td-heading-self-link" href="#quick-comparison" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;&lt;code&gt;.userInitiated&lt;/code&gt;&lt;/th&gt;
 &lt;th&gt;&lt;code&gt;.utility&lt;/code&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Priority&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;High&lt;/td&gt;
 &lt;td&gt;Low-medium&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;User waiting?&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Yes, directly&lt;/td&gt;
 &lt;td&gt;Aware but not blocked&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Duration&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&amp;lt; a few seconds&lt;/td&gt;
 &lt;td&gt;Seconds to minutes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;CPU allocation&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Aggressive&lt;/td&gt;
 &lt;td&gt;Conservative&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Battery impact&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Higher&lt;/td&gt;
 &lt;td&gt;Lower&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Thread pool&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Higher-priority threads&lt;/td&gt;
 &lt;td&gt;Lower-priority threads&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id="rule-of-thumb"&gt;Rule of thumb&lt;a class="td-heading-self-link" href="#rule-of-thumb" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8f5902;font-style:italic"&gt;// User tapped &amp;#34;Load&amp;#34; and is staring at a spinner → userInitiated&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#000"&gt;DispatchQueue&lt;/span&gt;&lt;span style="color:#000;font-weight:bold"&gt;.&lt;/span&gt;&lt;span style="color:#000"&gt;global&lt;/span&gt;&lt;span style="color:#000;font-weight:bold"&gt;(&lt;/span&gt;&lt;span style="color:#000"&gt;qos&lt;/span&gt;&lt;span style="color:#000;font-weight:bold"&gt;:&lt;/span&gt; &lt;span style="color:#000;font-weight:bold"&gt;.&lt;/span&gt;&lt;span style="color:#000"&gt;userInitiated&lt;/span&gt;&lt;span style="color:#000;font-weight:bold"&gt;).&lt;/span&gt;&lt;span style="color:#000"&gt;async&lt;/span&gt; &lt;span style="color:#000;font-weight:bold"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#204a87;font-weight:bold"&gt;let&lt;/span&gt; &lt;span style="color:#000"&gt;data&lt;/span&gt; &lt;span style="color:#000;font-weight:bold"&gt;=&lt;/span&gt; &lt;span style="color:#000"&gt;loadCriticalData&lt;/span&gt;&lt;span style="color:#000;font-weight:bold"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#000;font-weight:bold"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8f5902;font-style:italic"&gt;// Background sync / download with a progress bar → utility&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#000"&gt;DispatchQueue&lt;/span&gt;&lt;span style="color:#000;font-weight:bold"&gt;.&lt;/span&gt;&lt;span style="color:#000"&gt;global&lt;/span&gt;&lt;span style="color:#000;font-weight:bold"&gt;(&lt;/span&gt;&lt;span style="color:#000"&gt;qos&lt;/span&gt;&lt;span style="color:#000;font-weight:bold"&gt;:&lt;/span&gt; &lt;span style="color:#000;font-weight:bold"&gt;.&lt;/span&gt;&lt;span style="color:#000"&gt;utility&lt;/span&gt;&lt;span style="color:#000;font-weight:bold"&gt;).&lt;/span&gt;&lt;span style="color:#000"&gt;async&lt;/span&gt; &lt;span style="color:#000;font-weight:bold"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#000"&gt;downloadLargeFile&lt;/span&gt;&lt;span style="color:#000;font-weight:bold"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#000;font-weight:bold"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;If you use &lt;code&gt;.userInitiated&lt;/code&gt; for everything&lt;/strong&gt;, you waste battery and CPU on non-urgent work. &lt;strong&gt;If you use &lt;code&gt;.utility&lt;/code&gt; for user-blocking tasks&lt;/strong&gt;, the UI will feel sluggish because the system may deprioritize the work.&lt;/p&gt;</description></item><item><title>Security-Scoped URLs</title><link>https://rawcull.netlify.app/blog/2026/02/05/security-scoped-urls/</link><pubDate>Thu, 05 Feb 2026 00:00:00 +0000</pubDate><guid>https://rawcull.netlify.app/blog/2026/02/05/security-scoped-urls/</guid><description>&lt;p&gt;Security-scoped URLs are a cornerstone of macOS App Sandbox security. RawCull uses them to gain persistent, user-approved access to source and destination folders while remaining fully sandbox-compliant. This article walks through exactly how the implementation works, tracing the code from user interaction through to file operations.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="what-are-security-scoped-urls"&gt;What Are Security-Scoped URLs?&lt;a class="td-heading-self-link" href="#what-are-security-scoped-urls" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A security-scoped URL is a special file URL that carries a cryptographic capability granted by macOS, representing explicit user consent to access a specific file or folder. Without it, a sandboxed app cannot read or write anything outside its own container.&lt;/p&gt;</description></item><item><title>Compiling RawCull</title><link>https://rawcull.netlify.app/blog/2026/02/01/compiling-rawcull/</link><pubDate>Sun, 01 Feb 2026 00:00:00 +0000</pubDate><guid>https://rawcull.netlify.app/blog/2026/02/01/compiling-rawcull/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;a class="td-heading-self-link" href="#overview" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;div class="alert alert-info" role="alert"&gt;
&lt;p&gt;There are three methods to compile RawCull: one without an Apple Developer account and two with an Apple Developer account. Regardless of the method used, it is straightforward to compile RawCull, as it is not dependent on any third-party code or library.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The easiest method is by using the included Makefile. The default make in &lt;code&gt;/usr/bin/make&lt;/code&gt; does the job.&lt;/p&gt;
&lt;h3 id="compile-by-make"&gt;Compile by make&lt;a class="td-heading-self-link" href="#compile-by-make" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you have an Apple Developer account, you should open the RawCull project and replace the &lt;code&gt;Signing &amp;amp; Capabilities&lt;/code&gt; section with your own Apple Developer ID before using &lt;code&gt;make&lt;/code&gt; and the procedure outlined below.&lt;/p&gt;</description></item></channel></rss>