Close Menu
geekfence.comgeekfence.com
    What's Hot

    The rising Artificial Intelligence (AI) consumption costs: from innovation to inflation

    May 7, 2026

    Health and wellness influencers dominate social media. A new report shines a light on who they actually are.

    May 7, 2026

    The Best Risk Mitigation Strategy in Data? A Single Source of Truth – O’Reilly

    May 7, 2026
    Facebook X (Twitter) Instagram
    • About Us
    • Contact Us
    Facebook Instagram
    geekfence.comgeekfence.com
    • Home
    • UK Tech News
    • AI
    • Big Data
    • Cyber Security
      • Cloud Computing
      • iOS Development
    • IoT
    • Mobile
    • Software
      • Software Development
      • Software Engineering
    • Technology
      • Green Technology
      • Nanotechnology
    • Telecom
    geekfence.comgeekfence.com
    Home»iOS Development»Introducing SwiftPorts | Cocoanetics
    iOS Development

    Introducing SwiftPorts | Cocoanetics

    AdminBy AdminMay 7, 2026No Comments10 Mins Read2 Views
    Facebook Twitter Pinterest LinkedIn Telegram Tumblr Email
    Introducing SwiftPorts | Cocoanetics
    Share
    Facebook Twitter LinkedIn Pinterest Email


    When I was building SwiftBash I made surprisingly quick headway on the basic CLI utilities — jq, awk, sed, grep. Each one is a small, well-scoped language, and once you sit down with the spec it really is just a parser and an evaluator.

    Then I hit a wall. The two CLIs I reach for most as a working developer aren’t tiny languages — they’re gh and glab, the GitHub and GitLab clients. And right next to them, the granddaddy of all dev CLIs: git. These aren’t 2,000-line tools. gh alone is roughly fifty thousand lines of Go, with subcommand trees, OAuth flows, REST + GraphQL clients, pagination, archive extraction, jq filtering — the works. Reimplementing all of that by hand felt like a year of evenings.

    But the source code is right there on GitHub. And I have a coding agent. So I began to wonder: shouldn’t Opus 4.7 1M (extra-high) be able to translate cli/cli into Swift for me, given the original as ground truth?

    It turns out: yes. That’s where SwiftPorts comes from.

    What “porting” actually looks like

    The workflow is duller than it sounds. I point the agent at a subcommand in the upstream Go source, give it the existing Swift module’s conventions, and ask it to produce the equivalent under swift-argument-parser. Then I run the resulting binary side-by-side with the original tool and look for divergence: different exit codes, different output framing, missing flags, wrong default behaviour.

    A working example from this morning: a Codex reviewer caught that our gh api --input was silently dropping -f/-F field flags, where upstream documents that those flags should be appended to the endpoint’s query string. Two-line bug, one acceptance test, merged. That kind of paper-cut parity bug is the entire game. Build the surface, then beat it against reality until it behaves identically.

    I started with gh and through a few iterations got most of the useful operations working: repo, pr, issue, release, workflow, run, gist, project, label, org, cache, variable, secret, ssh-key, gpg-key, search, config, auth. Some obscure corners — like gh attestation — I left for later. If you genuinely use gh attestation in anger, please tell me what it’s good for.

    Next was glab for GitLab (which I run self-hosted), and a pattern emerged: a lot of the host-agnostic plumbing — TTY detection, ANSI handling, the keychain wrapper, the abstraction over git for the clone-and-checkout dance — was duplicated between the two. So we factored that out into ForgeKit, which both GhCommand and GlabCommand now share.

    The git-aware ones, and SwiftGit on libgit2

    There’s a class of gh/glab operations that aren’t pure remote API calls — they’re “git-aware.” gh pr create needs to know your current branch and the remote’s owner/name. gh repo clone shells out to git clone. gh pr checkout does a git fetch followed by git checkout. glab mr checkout is the same shape.

    Upstream gh solves this by literally invoking /usr/bin/git as a subprocess. That works, but it’s not embeddable: no Process on iOS, no system git binary in a sandboxed Mac App Store app.

    A while back I had some successful experiments with libgit2, the C reimplementation of git. So I wondered: could the agent build a complete git client on top of it? And — yes, it could. Most of the work, it turns out, is mechanical wiring: take an ArgumentParser flag, map it to the corresponding git_* option struct field, hand the struct to libgit2, translate the result back. There are sharp edges around credential callbacks, signature resolution, and the per-op git_libgit2_opts global state, but the bulk of git init / clone / fetch / pull / push / status / log / diff / show / commit / merge / rebase / cherry-pick / reset / checkout / switch / restore / add / rm / mv / clean / stash / tag / branch / remote / config / rev-parse / ls-files / ls-tree / cat-file / describe / blame / apply / reflog is just plumbing.

    That became the SwiftGit module, with its own git executable. ForgeKit’s GitClient protocol lets gh/glab swap between a ProcessGitClient (the “shell out” path) and SwiftGit’s libgit2-backed in-process client without changing a line of subcommand code. On iOS, SwiftGit is the only path that works.

    The compression rabbit hole

    A handful of gh operations need decompression. gh release download should auto-extract .zip / .tar.gz / .tar.bz2 / .tar.xz / .tar.zst / .tar.lz4 assets without subprocess calls; gh run download and gh run view --log need to crack open ZIP-format workflow artifacts.

    I started with ZIPFoundation, and that worked beautifully — until I tried to build for Android. (I now routinely build for Android and Windows in CI, because nothing focuses the mind like five green checkmarks across iOS, Mac, Linux, Windows and Android)

    Marc Prud’hommeaux dropped a great suggestion in issue #6: use his Swift-friendly fork of libarchive instead of ZIPFoundation. I had Opus evaluate it, and the conclusion was: this changes the scope. libarchive doesn’t just give you Zip — it gives you tar with auto-detected gzip / bzip2 / xz / zstd / lz4 filtering, plus 7z, plus a half-dozen other formats nobody’s asking for. So instead of one ZipKit umbrella we ended up with a whole compression family: ZipKit, TarKit, GzipKit, Bzip2Kit, XzKit, ZstdKit, Lz4Kit. Each one ships its own kit (the library) plus a command (the CLI), plus the one-letter aliases (gunzip, zcat, bunzip2, xzcat, unzstd, lz4cat, …) you’d find in coreutils.

    The fun edge case: not every codec ships as a system library on iOS. liblzma and liblz4 in particular aren’t separately available there. So those kits look at the platform at compile time and route through Apple’s Compression framework instead — XzKit uses Compression.framework‘s LZMA path; Lz4Kit uses COMPRESSION_LZ4_RAW. The result is that an iOS app can gh release download a .tar.xz asset and unpack it entirely in-process, with no subprocess and no missing-codec apology.

    JqKit, stolen from SwiftBash

    gh api --jq and glab api --jq are how you actually use those commands productively against GraphQL. Upstream gh runs the response through a real jq library; the lazy port would shell out to /usr/bin/jq.

    I’d already written a pure-Swift jq parser + evaluator + builtins for SwiftBash, so I stole it back and wrapped it in JqKit — a Jq.eval / Jq.evalString facade with no system C dependency, callable from any Swift context. gh api --jq '.full_name' now runs the filter in-process. So does glab api --jq.

    This is the part of SwiftPorts that’s genuinely mutual with SwiftBash. Both projects benefit; neither owns the code.

    Sandboxing and async everything

    Once the surface area was wide enough, I started prep work for plugging these tools into SwiftBash. That meant two big mechanical refactors.

    The first was making everything async-throwing and adding Task.checkCancellation() calls inside every hot loop — the recursive directory walks in tar, the per-page pagination loops in gh search, the byte-pump loops in the compression engines. The user-facing payoff is that SwiftBash can Ctrl-C a running operation and have it actually stop in milliseconds, instead of waiting out the rest of a 50,000-file walk.

    The second was a sandbox. SwiftBash, when embedded in an iOS or sandboxed-Mac app, can’t run with the host’s full filesystem and environment — it has to be confined to a folder, with environment variables and argv strictly under the embedder’s control.

    What I really wanted was for the OS to provide me a sandbox primitive I could just enter. macOS has sandbox_init and Apple’s seatbelt profiles, but they’re private API and not what you want to be shipping on the App Store. iOS doesn’t expose anything similar at all. So I had to build it in user space.

    The result is the Sandbox module, about 650 lines of Swift in two files. It’s a default-deny @TaskLocal policy: when Sandbox.current is non-nil, every URL handed to the gated I/O sites in SwiftPorts has to authorize through Sandbox.authorize(_:), and every ambient reach (environment variables, process arguments, region directories like ~/.config) consults the sandbox’s own values rather than the host’s. Two factories cover the common cases: Sandbox.rooted(at:) for single-folder confinement on Mac/Linux, and Sandbox.appContainer(id:) for iOS where the standard documents/temporary/caches/group folders form the natural perimeter.

    Crucially, the sandbox also intercepts environment variable reads. The naïve sandbox is the one you can escape with HOME=/etc git config --global ... — point a tool at a “different” home directory and watch it write outside your perimeter. SwiftPorts code never reads ProcessInfo.processInfo.environment directly; it reads through the sandbox, which by default returns [:] and only returns host values if the embedder explicitly asks for passthrough. I went through every existing call site with a regression test that bans ambient ProcessInfo and FileManager access in Sources/, so the perimeter doesn’t bit-rot.

    When Sandbox.current is nil — which is the case for everyone running the binaries directly from the command line — every gate is a no-op and behaviour is identical to the original. You only pay for the sandbox if you’re embedding.

    So… where does this all live?

    SwiftPorts is a separate repo from SwiftBash on purpose. Developing the dev tooling separately keeps each tractable and lets the ports be used as true CLIs, or embedded into apps without dragging in an interpreter. You could, for example, build a real GitHub client for iPad on top of GitHub + SwiftGit + JqKit and never need SwiftBash at all.

    I’m still figuring out where the line is. My current feeling: anything that’s a port of a real CLI utility belongs in *-Ports. SwiftBash should be just the interpreter — the bash language, expansions, redirections, control flow — pulling in builtins by Swift Package dependency. SwiftScript, the Swift interpreter, fits the same shape: another language frontend that consumes the same builtins.

    If that lands, the picture I see is a pull-down bash shell on my iPad with a coding agent in the next pane, fully sandboxed and App Store-legal. SwiftBash for the shell, SwiftScript for the inline-Swift-snippet escape hatch, SwiftPorts for the actual work — git, gh, tar, jq. That’s the unifying daydream.

    Right now SwiftPorts is still very much a solution in search of a problem. I have a nebulous vision of a command-center app that reads issues from GitLab and GitHub, hands them to coding agents, deals with review comments, watches CI, and fixes things that come up there — a universal Mac/iOS app I could keep running on my iPad to babysit my OSS while AFK. Maybe that’s where this goes.

    What I actually need from you

    All three projects — SwiftPorts, SwiftBash, SwiftScript — are mostly in want of real use cases that exercise them against the originals. The goal is for the ported utilities to behave exactly like the tools they replace. If you run gh or glab or git from SwiftPorts and you spot anything — a flag the upstream tool accepts that ours rejects, an output format that’s subtly different, a default that diverges, an exit code that doesn’t match — that’s the gold. Open an issue and I’ll feed it to the agent. The closer we get to invisible parity, the more useful any of this becomes.

    The repo is on GitHub. Tell me what you’d build with it — and tell me where it gets things wrong.

    Like this:

    Like Loading…

    Related


    Categories: Updates



    Source link

    Share. Facebook Twitter Pinterest LinkedIn Tumblr Email

    Related Posts

    Awesome native Xcode extensions – The.Swift.Dev.

    May 6, 2026

    ios – Flutter UI not matching design: top-right badge not properly attached to card border

    May 5, 2026

    Four Green Checkmarks: GitHub CI for macOS, iOS, Linux, and Windows

    May 1, 2026

    Swift enum all values – The.Swift.Dev.

    April 30, 2026

    ios – AppShortcut is shown in Shortcuts app but not in Spotlight

    April 29, 2026

    How to make a Swift framework?

    April 25, 2026
    Top Posts

    Understanding U-Net Architecture in Deep Learning

    November 25, 202538 Views

    Hard-braking events as indicators of road segment crash risk

    January 14, 202626 Views

    Redefining AI efficiency with extreme compression

    March 25, 202625 Views
    Don't Miss

    The rising Artificial Intelligence (AI) consumption costs: from innovation to inflation

    May 7, 2026

    AI’s cost paradox Something has quietly changed in how buyers talk about Artificial Intelligence (AI).…

    Health and wellness influencers dominate social media. A new report shines a light on who they actually are.

    May 7, 2026

    The Best Risk Mitigation Strategy in Data? A Single Source of Truth – O’Reilly

    May 7, 2026

    Build streaming applications on Amazon Managed Service for Apache Flink with AI-assisted guidance

    May 7, 2026
    Stay In Touch
    • Facebook
    • Instagram
    About Us

    At GeekFence, we are a team of tech-enthusiasts, industry watchers and content creators who believe that technology isn’t just about gadgets—it’s about how innovation transforms our lives, work and society. We’ve come together to build a place where readers, thinkers and industry insiders can converge to explore what’s next in tech.

    Our Picks

    The rising Artificial Intelligence (AI) consumption costs: from innovation to inflation

    May 7, 2026

    Health and wellness influencers dominate social media. A new report shines a light on who they actually are.

    May 7, 2026

    Subscribe to Updates

    Please enable JavaScript in your browser to complete this form.
    Loading
    • About Us
    • Contact Us
    • Disclaimer
    • Privacy Policy
    • Terms and Conditions
    © 2026 Geekfence.All Rigt Reserved.

    Type above and press Enter to search. Press Esc to cancel.