<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Im Woojin</title>
    <description>The latest articles on Forem by Im Woojin (@rakkunn).</description>
    <link>https://forem.com/rakkunn</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3860147%2F8f75007d-4931-48ca-881c-915ce86430f2.jpeg</url>
      <title>Forem: Im Woojin</title>
      <link>https://forem.com/rakkunn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/rakkunn"/>
    <language>en</language>
    <item>
      <title>DualClip v1.2.6 — Stability Fix, Homebrew</title>
      <dc:creator>Im Woojin</dc:creator>
      <pubDate>Tue, 14 Apr 2026 07:57:34 +0000</pubDate>
      <link>https://forem.com/rakkunn/dualclip-v126-stability-fix-homebrew-47ef</link>
      <guid>https://forem.com/rakkunn/dualclip-v126-stability-fix-homebrew-47ef</guid>
      <description>&lt;p&gt;It's been a busy couple of days for DualClip. What started as a frustrating crash investigation turned into a series of meaningful improvements — from fixing a deep resource-bundling bug to shipping Homebrew support. Here's a rundown of everything that changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Crash That Wouldn't Quit
&lt;/h2&gt;

&lt;p&gt;Shortly after v1.2.0, DualClip started crashing on launch for some users. The crash report pointed to Bundle.module inside the KeyboardShortcuts dependency — a fatalError triggered because the app couldn't locate its resource bundle at runtime.&lt;/p&gt;

&lt;p&gt;The root cause turned out to be a subtle mismatch in how Swift Package Manager bundles resources for standalone .app builds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SPM generates a file called resource_bundle_accessor.swift that looks for resources at Bundle.main.bundleURL — which resolves to the .app/ root directory.&lt;/li&gt;
&lt;li&gt;But macOS codesigning requires all resources to live inside Contents/Resources/, not the app root. Placing bundles at the root causes an "unsealed contents" signing error.&lt;/li&gt;
&lt;li&gt;So the resources were in Contents/Resources/, but the code was looking at .app/ — and finding nothing.
The fix was surgical: after the initial build, we patch the generated accessor to use Bundle.main.resourceURL! (which points to Contents/Resources/), then recompile just the affected module and re-link the executable. SPM regenerates the accessor on every full build, so we had to carefully avoid triggering a rebuild after patching. The final binary is verified with strings to confirm the patch is baked in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This fix ships in v1.2.6 and the crash is fully resolved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Homebrew Support
&lt;/h2&gt;

&lt;p&gt;DualClip is now installable via Homebrew:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;brew install RAKKUNN/tap/dualclip&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We set up a dedicated Homebrew Tap with a Cask formula that points to the latest signed and notarized release. The CI pipeline automatically updates the formula (version + SHA256) whenever a new release is tagged — zero manual steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Makes DualClip Different
&lt;/h2&gt;

&lt;p&gt;After analyzing 57 macOS clipboard managers, one thing stood out: every single one is a clipboard history manager. DualClip is the only one that uses dedicated slots.&lt;/p&gt;

&lt;p&gt;The difference matters:&lt;/p&gt;

&lt;p&gt;History managers record everything you copy and let you scroll back through it. Great for recall, but adds complexity — databases, search UI, sync, storage limits.&lt;br&gt;
DualClip gives you 3 fixed slots with instant keyboard access. You decide where to copy, not when to find it later. No history, no scrolling, no search.&lt;br&gt;
It's also one of the few clipboard tools that stores absolutely nothing to disk. Everything lives in RAM and vanishes on quit. No network access, no telemetry, no cloud sync. &lt;/p&gt;
&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;brew install RAKKUNN/tap/dualclip&lt;/code&gt;&lt;br&gt;
Or grab the latest release from GitHub.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/RAKKUNN" rel="noopener noreferrer"&gt;
        RAKKUNN
      &lt;/a&gt; / &lt;a href="https://github.com/RAKKUNN/DualClip" rel="noopener noreferrer"&gt;
        DualClip
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A lightweight macOS app for multi-slot clipboard management
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/RAKKUNN/DualClip/icon.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FRAKKUNN%2FDualClip%2FHEAD%2Ficon.png" alt="DualClip Icon" width="128" height="128"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;DualClip&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;
  A lightweight macOS menu bar app that provides &lt;b&gt;multi-slot clipboard management&lt;/b&gt;.&lt;br&gt;
  Unlike history-based clipboard managers, DualClip gives you instant access to dedicated clipboard slots via customizable keyboard shortcuts
&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/RAKKUNN/DualClip/actions/workflows/ci.yml/badge.svg"&gt;&lt;img src="https://github.com/RAKKUNN/DualClip/actions/workflows/ci.yml/badge.svg" alt="CI"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/6bffd51139ffc392eddcd88884109841bddeb7332c97055a073d66df7fadf064/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6d61634f532d31332532422d626c7565"&gt;&lt;img src="https://camo.githubusercontent.com/6bffd51139ffc392eddcd88884109841bddeb7332c97055a073d66df7fadf064/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6d61634f532d31332532422d626c7565" alt="macOS 13+"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/8c972106af96ab0d4f2778157576ab9728bc8b0735969ac8963a503781b0e62b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f53776966742d352e392532422d6f72616e6765"&gt;&lt;img src="https://camo.githubusercontent.com/8c972106af96ab0d4f2778157576ab9728bc8b0735969ac8963a503781b0e62b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f53776966742d352e392532422d6f72616e6765" alt="Swift 5.9+"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/5caa455d8debc46fb23abbadb45a733a937f3910a73fc875c2f7820468e1bb54/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e"&gt;&lt;img src="https://camo.githubusercontent.com/5caa455d8debc46fb23abbadb45a733a937f3910a73fc875c2f7820468e1bb54/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e" alt="License: MIT"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;3 Clipboard Slots&lt;/strong&gt;: Slot A (system default), Slot B, and Slot C&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customizable Shortcuts&lt;/strong&gt;: No hardcoded key conflicts — configure your own shortcuts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic Paste&lt;/strong&gt;: Seamlessly pastes from any slot without corrupting your system clipboard&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Menu Bar Popover&lt;/strong&gt;: Quick-glance view of all slot contents with previews&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy First&lt;/strong&gt;: All data lives in RAM only — nothing is persisted to disk&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Network Access&lt;/strong&gt;: No telemetry, no analytics, no internet communication&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Demo&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/RAKKUNN/DualClip/test_dualclip.gif"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FRAKKUNN%2FDualClip%2FHEAD%2Ftest_dualclip.gif" alt="DualClip Demo" width="600"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/RAKKUNN/DualClip/test_dualclip_image.gif"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FRAKKUNN%2FDualClip%2FHEAD%2Ftest_dualclip_image.gif" alt="DualClip Image Support Demo" width="600"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Default Shortcuts&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;br&gt;
&lt;thead&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;br&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;/thead&gt;
&lt;br&gt;
&lt;tbody&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;Copy to Slot B&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;⌥⌘C&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;Paste from Slot B&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;⌥⌘V&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;Copy to Slot C&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;⌃⌘C&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;Paste from Slot C&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;⌃⌘V&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;/tbody&gt;
&lt;br&gt;
&lt;/table&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;All shortcuts are fully customizable in &lt;strong&gt;Settings &amp;gt; Shortcuts&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How It Works&lt;/h2&gt;

&lt;/div&gt;


&lt;ol&gt;

&lt;li&gt;

&lt;strong&gt;Slot A&lt;/strong&gt; automatically mirrors the…&lt;/li&gt;

&lt;/ol&gt;
&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/RAKKUNN/DualClip" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;&lt;br&gt;
Signed and notarized by Apple — no Gatekeeper warnings.

&lt;p&gt;DualClip is open source under the MIT license. Issues, feedback, and contributions are welcome.&lt;/p&gt;

&lt;p&gt;Thank you, have a nice day!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>swift</category>
      <category>showdev</category>
    </item>
    <item>
      <title>DualClip - Update_2026.04.12.</title>
      <dc:creator>Im Woojin</dc:creator>
      <pubDate>Sun, 12 Apr 2026 07:48:23 +0000</pubDate>
      <link>https://forem.com/rakkunn/dualclip-update20260412-4pb7</link>
      <guid>https://forem.com/rakkunn/dualclip-update20260412-4pb7</guid>
      <description>&lt;p&gt;Last week, I shared the first round of updates for DualClip, my open-source macOS clipboard manager. Since then, I’ve crossed off three major items from the roadmap—including the one I’m most excited about: you can finally download DualClip without needing to build it from source.&lt;/p&gt;

&lt;p&gt;Here is the breakdown of what’s new in v1.1.0.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔒 Secure Input Field Detection
&lt;/h2&gt;

&lt;p&gt;This was at the top of my "What’s Next" list for a reason: Privacy.&lt;/p&gt;

&lt;p&gt;When macOS has a password field focused, the system activates a mode called Secure Event Input. DualClip now checks for this state before every copy and paste operation. If you're typing a password into Safari, 1Password, or a terminal sudo prompt, DualClip silently steps back.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kt"&gt;Swift&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;isSecureInputActive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;IsSecureEventInputEnabled&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The guard is dead simple—one line in handleCopy and one in handlePaste:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kt"&gt;Swift&lt;/span&gt;
&lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="kt"&gt;AccessibilityService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isSecureInputActive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s the kind of feature that, when it works correctly, you never notice. And that’s exactly the point.&lt;/p&gt;

&lt;h2&gt;
  
  
  📦 Prebuilt Binaries (No Xcode Required!)
&lt;/h2&gt;

&lt;p&gt;The biggest friction point in my original post was: "There's no prebuilt binary yet." Starting with v1.1.0, every release is signed with a Developer ID certificate and notarized by Apple. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;No more Gatekeeper warnings.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No "Open Anyway" dance in System Settings.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Just download, drag to Applications, and go.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🏗 The CI/CD Saga: 3 Bugs and a Notarization Pipeline
&lt;/h2&gt;

&lt;p&gt;Getting the automated release pipeline working was... an adventure. I’m using GitHub Actions to handle the heavy lifting. The workflow triggers on version tags (v*.&lt;em&gt;.&lt;/em&gt;) and follows this sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Build: swift build -c release on a macOS 14 ARM runner.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Import: Decode the .p12 certificate from GitHub Secrets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bundle: Assemble the .app structure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sign: codesign with hardened runtime and a timestamp.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Notarize: Submit to Apple, poll for completion, and staple the ticket.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Sounds clean on paper. In practice, it took four attempts and taught me three very humbling lessons:&lt;/p&gt;

&lt;p&gt;Bug 1: The One-Character Typo&lt;br&gt;
I spent 10 minutes staring at a 401 Unauthorized error from Apple’s API. It turned out to be a typo in my app-specific password secret: fjhk vs tihk. CI/CD reminds you that your eyes see what they want to see, not what’s actually there.&lt;/p&gt;

&lt;p&gt;Bug 2: The Service Outage&lt;br&gt;
Three consecutive submissions got stuck at "In Progress" for hours. I thought my script was hung. It turns out Apple's notarization service was actually having a global outage. Even the best pipeline can’t fix a broken cloud.&lt;/p&gt;

&lt;p&gt;Bug 3: The awk Failure&lt;br&gt;
My polling script used awk '{print $2}' to extract the status from notarytool info. However, the status string for a pending job is In Progress (two words). awk captured only "In," which matched nothing in my logic.&lt;br&gt;
The Fix: Switched to sed 's/.*status: //' to grab the full string.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔄 Updated Roadmap
&lt;/h2&gt;

&lt;p&gt;Feature Status&lt;br&gt;
Secure input field detection    ✅ Shipped&lt;br&gt;
RAM zeroing on termination  ✅ Shipped&lt;br&gt;
Image/rich text support ✅ Shipped&lt;br&gt;
GitHub Actions CI/CD + Notarization ✅ Shipped&lt;br&gt;
Homebrew Cask distribution  🔜 Next&lt;br&gt;
Sparkle auto-update 📅 Planned&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it out
&lt;/h2&gt;

&lt;p&gt;If you’ve been waiting for a downloadable build, it’s ready for you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to the Latest Releases.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Download DualClip-1.1.0-arm64.zip.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unzip, move to Applications, and launch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Grant Accessibility permission when prompted.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;🔗 GitHub: &lt;a href="https://github.com/RAKKUNN/DualClip" rel="noopener noreferrer"&gt;https://github.com/RAKKUNN/DualClip&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you find a bug or have a feature request, feel free to open an issue or a PR!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>swift</category>
    </item>
    <item>
      <title>DualClip - Update_2026.04.04.</title>
      <dc:creator>Im Woojin</dc:creator>
      <pubDate>Sat, 04 Apr 2026 10:54:46 +0000</pubDate>
      <link>https://forem.com/rakkunn/dualclip-update20260404-39lp</link>
      <guid>https://forem.com/rakkunn/dualclip-update20260404-39lp</guid>
      <description>&lt;h1&gt;
  
  
  DualClip Update — Beyond Text, and a 150ms Correction
&lt;/h1&gt;

&lt;p&gt;A few hours ago, I shared &lt;a href="https://github.com/RAKKUNN/DualClip" rel="noopener noreferrer"&gt;DualClip&lt;/a&gt;, a slot-based clipboard manager for macOS. Thank you to everyone who checked it out!&lt;/p&gt;

&lt;p&gt;Since then, I've shipped a few meaningful updates that I didn't cover in the original post. I also owe you a small correction. Let me walk through both.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔧 First, a correction: 50ms → 150ms
&lt;/h2&gt;

&lt;p&gt;In my first post, I wrote that the Atomic Paste operation completes "in less than 50ms." That was wrong. The actual restore delay is &lt;strong&gt;150ms&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// 150ms is an empirically safe value to prevent race conditions.&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;restoreDelayMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;150ms was chosen empirically to avoid a race condition — if the clipboard is restored before the target application finishes reading it, the paste silently fails. 50ms sounded cooler, but 150ms is what actually works reliably. Sorry about that.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡ How Atomic Paste actually works
&lt;/h2&gt;

&lt;p&gt;Since this is the core mechanic of DualClip, I figured it deserves a proper breakdown. When you press the hotkey to paste from Slot B, here's what happens under the hood:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Backup&lt;/strong&gt; — The entire system clipboard (Slot A) is deep-copied into a temporary buffer. Not just text — all &lt;code&gt;NSPasteboardItem&lt;/code&gt; types and their raw data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Swap&lt;/strong&gt; — Slot B's content is written to the system clipboard, replacing whatever was there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Simulate&lt;/strong&gt; — A &lt;code&gt;CGEvent&lt;/code&gt;-based ⌘V keystroke is posted to the HID system. This is why DualClip requires Accessibility permission — it's literally pressing keys on your behalf.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Restore&lt;/strong&gt; — After 150ms, the backup is written back to the system clipboard. Your original ⌘C content is untouched.&lt;/p&gt;

&lt;p&gt;The entire flow is invisible to the user. You press a hotkey, text appears, and your clipboard stays exactly as it was.&lt;/p&gt;




&lt;h2&gt;
  
  
  🖼 Multi-content type support
&lt;/h2&gt;

&lt;p&gt;The first post only showed text workflows, which gave the impression that DualClip is text-only. It's not — it now supports &lt;strong&gt;four content types&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Plain text&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Code snippets, URLs, API keys&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rich text (RTF)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Formatted text with bold, colors, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Images&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Screenshots, design assets from Figma&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;File URLs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;File paths copied from Finder&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each slot stores the raw &lt;code&gt;NSPasteboardItem&lt;/code&gt; data faithfully, so what you copy is exactly what you get back — formatting, metadata, and all.&lt;/p&gt;

&lt;p&gt;This means you can now keep a screenshot in Slot B and a HEX code in Slot C, then paste them alternately into your editor without touching the mouse.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔐 RAM Zeroing on termination
&lt;/h2&gt;

&lt;p&gt;In the first post, I mentioned that all data lives in RAM only. That's still true, but I've taken it a step further.&lt;/p&gt;

&lt;p&gt;When DualClip quits, it doesn't just release memory — it &lt;strong&gt;overwrites every byte with zeros&lt;/strong&gt; before deallocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;secureWipe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pasteboardItems&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;forType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;withUnsafeBytes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;rawBuffer&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
                        &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;baseAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rawBuffer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;baseAddress&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;mutable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UnsafeMutableRawPointer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mutating&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;baseAddress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="nf"&gt;memset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mutable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rawBuffer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is called on &lt;code&gt;applicationWillTerminate&lt;/code&gt; for all three slots. If you temporarily stored an API key or password in a slot, it won't linger in physical memory after the app closes.&lt;/p&gt;

&lt;p&gt;To summarize the privacy model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No disk writes.&lt;/strong&gt; Nothing is persisted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No network.&lt;/strong&gt; Zero external communication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RAM zeroing.&lt;/strong&gt; Memory is scrubbed on exit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open source.&lt;/strong&gt; You can verify all of the above yourself.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🏗 CI pipeline
&lt;/h2&gt;

&lt;p&gt;I've added a GitHub Actions workflow that runs on every push and PR to &lt;code&gt;main&lt;/code&gt;. It builds the project in both Debug and Release configurations on macOS 14 with Swift 5.9.&lt;/p&gt;

&lt;p&gt;It's a simple setup — no notarization or automatic releases yet — but it catches build regressions before they land.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠 Building from source
&lt;/h2&gt;

&lt;p&gt;There's no prebuilt binary yet, but building is straightforward if you have Xcode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/RAKKUNN/DualClip.git
&lt;span class="nb"&gt;cd &lt;/span&gt;DualClip
open Package.swift
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hit ▶ Run in Xcode. On first launch, macOS will ask for Accessibility permission — this is required for the CGEvent keystroke simulation that powers Atomic Paste.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Secure input field detection (auto-disable in password fields)&lt;/li&gt;
&lt;li&gt;Homebrew Cask distribution&lt;/li&gt;
&lt;li&gt;Sparkle auto-update&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;This is still a small, single-purpose tool, but I'm trying to get the details right. If you have feedback or want to contribute, the repo is open.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/RAKKUNN/DualClip" rel="noopener noreferrer"&gt;https://github.com/RAKKUNN/DualClip&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>swift</category>
    </item>
    <item>
      <title>DualClip: multi-slot clipboard manager for macOS</title>
      <dc:creator>Im Woojin</dc:creator>
      <pubDate>Fri, 03 Apr 2026 23:09:10 +0000</pubDate>
      <link>https://forem.com/rakkunn/dualclip-multi-slot-clipboard-manager-44f5</link>
      <guid>https://forem.com/rakkunn/dualclip-multi-slot-clipboard-manager-44f5</guid>
      <description>&lt;p&gt;&lt;strong&gt;First project with macOS &amp;amp; Swift&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I wanted to share &lt;strong&gt;DualClip&lt;/strong&gt;, a native macOS menu bar app I’ve been working on.&lt;/p&gt;

&lt;p&gt;While there are many great clipboard managers like Maccy or Paste, I found that most of them focus on "History"—searching through a vertical list of everything you've copied.&lt;/p&gt;

&lt;p&gt;I built DualClip because I needed something that works more like a "Workbench." Instead of picking from a menu, DualClip gives you dedicated slots (A, B, and C) that you can access instantly via global hotkeys. &lt;/p&gt;

&lt;p&gt;🚀 How it differs from history-based managers:&lt;/p&gt;

&lt;p&gt;No List Selection: You don't have to break your flow to search or click an item from a list. You use ⌥⌘C to save to Slot B and ⌥⌘V to paste it instantly. &lt;/p&gt;

&lt;p&gt;Atomic Paste: When you trigger a secondary slot, the app performs a high-speed "injection"—swapping the system clipboard, pasting, and restoring the original content in less than 50ms. &lt;/p&gt;

&lt;p&gt;Parallel Workflow: Perfect for developers moving IDs and Emails simultaneously, or translators working with source and target text in two separate slots. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpzfbwy1oifgvlbmx1f7n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpzfbwy1oifgvlbmx1f7n.png" alt=" " width="566" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmo0pbvbliklcfzzhgc7e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmo0pbvbliklcfzzhgc7e.png" alt=" " width="714" height="510"&gt;&lt;/a&gt;&lt;br&gt;
🔐 Privacy:&lt;/p&gt;

&lt;p&gt;As a security enthusiast, I designed this with transparency in mind:&lt;/p&gt;

&lt;p&gt;In-Memory Only: Clipboard data is stored strictly in RAM and is never written to disk.&lt;/p&gt;

&lt;p&gt;Zero Network Access: The app has no network permissions. No telemetry, no analytics, no external communication. &lt;/p&gt;

&lt;p&gt;🛠 Tech Stack:&lt;/p&gt;

&lt;p&gt;Language: Swift 5.9+ / SwiftUI &amp;amp; AppKit Hybrid&lt;/p&gt;

&lt;p&gt;The project is licensed under MIT, and I’d love to get some feedback or contributions from this community!&lt;/p&gt;

&lt;p&gt;Thank you for reading my small project!&lt;/p&gt;

&lt;p&gt;🔗 GitHub Repository: &lt;a href="https://github.com/RAKKUNN/DualClip" rel="noopener noreferrer"&gt;https://github.com/RAKKUNN/DualClip&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>swift</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
