Skip to content

Latest commit

 

History

83 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

QShare

Send and receive files between your Mac and nearby Android devices,
using Google's Quick Share — implemented from scratch.

Platform: macOS 26+ Swift 6 Dependencies: none License: 0BSD

QShare Send: nearby devices and a live transfer list    QShare Receive: visibility state, setup steps and transfer history


Android phones can share files with each other in two taps. Macs can't join in — Quick Share has no Apple client, and AirDrop doesn't speak to Android. QShare fills that gap: your Mac shows up in your phone's Quick Share sheet, and your phone shows up in QShare's device list.

Quick Share is undocumented, so this is a clean implementation written against a reverse-engineered specification — mDNS discovery, UKEY2 key agreement, AES-256-CBC + HMAC-SHA256 secure messages, chunked transfer — depending on nothing outside the OS.

Features

  • Send — pick a device and drag files onto it, or drop them on a device row
  • Receive — flip visibility on and your Mac appears on the phone
  • Text and links — saved to your downloads folder as files, never opened
  • PIN verification — a 4-digit code on both screens before anything moves
  • Per-sender auto-accept — opt in per device to skip the prompt (what it does and doesn't promise)
  • QR code — reach a phone that can't discover you
  • Menu-bar app — keeps running with the window closed
  • Command-line API — drive it from scripts or an agent (off by default)

Install

Needs macOS 26 or later, and both devices on the same Wi-Fi network.

Grab the latest QShare-macOS.dmg, open it, and drag QShare to Applications.

The build is ad-hoc signed but not notarized, so Gatekeeper blocks it the first time. On macOS 15 and later the old right-click → Open trick no longer works: open System Settings → Privacy & Security, find the notice about QShare being blocked, and press Open Anyway.

Using it

To receive, open the Receive tab and turn on visibility. The status line reflects whether your Mac is genuinely visible on the network, not just whether you flipped the switch. Then on your phone: share a file, pick your Mac, confirm the PIN.

To send, pick a device from the Send tab and drag files onto it. If the phone isn't listed, use the QR code — scanning it connects without discovery.

Received files land in ~/Downloads by default; change that in Settings.

⌘1 / ⌘2 Send / Receive
⇧⌘V Toggle visibility
⇧⌘O Open downloads folder
⌃⌥← / ⌃⌥→ Snap window to screen edge
⌘⌥I Build info

If your phone doesn't see your Mac

Usually this isn't a bug. Android discovers nearby devices by listening for Bluetooth LE advertisements, and macOS provides no API to emit them. Nothing in this app can fix that — it's a platform limitation, not an oversight.

In practice that means Mac → phone is the reliable direction, and phone → Mac needs a hand:

  • Make sure both devices are on the same Wi-Fi network — not one on 5 GHz and one on a guest SSID.
  • Check the router isn't running AP isolation (often called "client isolation"), which blocks devices from seeing each other.
  • Turn off any VPN on either device; most capture the traffic discovery needs.
  • Still nothing? Use the QR code. It's the designed answer to this, not a workaround — scanning it skips discovery entirely.

Security

Every transfer shows a 4-digit PIN on both screens before any bytes move. Matching it is what proves you're talking to the device you think you are.

Auto-accept is off by default, and it's worth knowing exactly what it gives up. Quick Share as implemented here exposes no verifiable device identity: UKEY2 keys are generated fresh for every handshake, and the certificate frames that would carry a persistent identity aren't in the specification this is built from. The only thing a sender proves is the name it chose to advertise.

So enabling auto-accept for "Pixel 8 Pro" means anything on your network calling itself Pixel 8 Pro is accepted without a prompt. That's why auto-accepted transfers still post a notification rather than landing silently, and why it's opt-in per device. For anything you care about, leave it off and confirm the PIN.

Received links are saved as files, never opened — otherwise auto-accept would let a device on your network open a URL on your Mac with no interaction at all.

Command line

QShare can host a small JSON API on 127.0.0.1:47821 for scripting and automation. It is off by default — enable it in Settings → Services.

While it's on, anything running under your account can ask QShare to send any file it can read to a nearby device. That's the point of the feature, and the reason it's opt-in.

ln -s "$(pwd)/App/Packaging/qshare" /usr/local/bin/qshare

qshare list                                   # devices currently visible
qshare send ~/photo.jpg --to "Pixel 8 Pro"    # blocks; exit 0 on success
qshare status --json                          # machine-readable

Requests are authenticated with a token in ~/.config/qshare/token. Full endpoint reference: docs/API.md.

How it works

Views (SwiftUI)  ──observe──▶  AppModel  ──▶  QuickShareService
                                                     │
                                          ┌──────────┴──────────┐
                                     MockService          QuickShareEngine
                                    (QS_MOCK=1)                 │
                                                    ┌───────────┴───────────┐
                                               Discovery              Transport
                                            mDNS · QR · TXT      framing · sessions
                                                                        │
                                                             Crypto ─── Wire
                                                            UKEY2 P-256  protobuf

Sources/QuickShareProtocol/ is the protocol; Sources/QuickShare/ is the app. The seam between them is one protocol (QuickShareService), which is also what makes the mock engine possible.

The whole thing is Swift 6 language mode: the transport runs on actors, the app on the main actor, and the compiler checks the boundary rather than convention.

The wire format is hand-written rather than generated — only a small fraction of the schema's messages are ever exchanged. Each is checked byte-identical against a reference encoder, and the parsers that face the network are tested against truncation, bit-flips, and random input.

See docs/ARCHITECTURE.md for the design and the protocol details worth knowing.

Build and develop

git clone https://github.com/kanin-design/QShare.git
cd QShare/App
./Packaging/build-app.sh
open build/QShare.app

The packaging step matters: real networking needs a proper .app bundle so macOS will grant local-network access. A bare swift run binary can't get it.

swift test                        # unit + protocol tests
swift test --sanitize=thread      # concurrency
QS_MOCK=1 swift run QuickShare    # UI work without a phone

QS_MOCK=1 runs a simulated engine that drives every UI state — devices, PINs, progress, completion — with no network at all. The screenshots above were taken with it.

./Packaging/make-dmg.sh builds the app and wraps it in a disk image, which is how releases are cut.

Credits

Quick Share is undocumented. The protocol was reverse-engineered and specified by @grishka, published at grishka/NearDrop and released into the public domain.

QShare implements the protocol from that specification — the code here is its own, but the knowledge isn't. See ATTRIBUTION.md for detail.

Not affiliated with or endorsed by Google. "Quick Share" and "Nearby Share" are their trademarks.

License

0BSD — use it, change it, ship it, sell it. No attribution required, no conditions attached. In the spirit of the work it's built on.

About

AirDrop-style file sharing between macOS and Android — a native, dependency-free macOS client for Google Quick Share (Nearby Share). Written in Swift 6 + SwiftUI.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages