A native BitTorrent client for macOS and iOS, built with SwiftUI over libtorrent using Swift/C++ interop.
cp Local.env.example Local.env # bundle id and team identifier; gitignored
make bootstrap # libtorrent submodule + Boost headers (~184 MB)
make openssl # OpenSSL xcframework (slow: five Configure+make runs)
make test # engine test suite
make run # build and launch the Mac appmake on its own lists everything. make config prints the bundle id and team
it resolved, which is the quickest way to check Local.env is being read.
Local.env is gitignored, so a team identifier never lands in the repository.
Precedence is environment, then Local.env, then the defaults in
Project.swift — so CI can override a developer's checkout without editing it.
The values reach the manifest as TUIST_-prefixed variables, and the prefix is
required rather than decorative: Tuist evaluates Project.swift in a sandbox
where ProcessInfo.environment is empty and reading Local.env from disk
fails, so that is the only channel available. One consequence is that plain
tuist generate does not read Local.env — use make generate, which
exports the variables first.
Everything else derives from the bundle id. The BGTaskScheduler identifiers and
the magnet URL type use $(PRODUCT_BUNDLE_IDENTIFIER) in the Info.plist, and
BackgroundCoordinator derives its constants from Bundle.main.bundleIdentifier,
so the two cannot drift — which matters because a mismatch there is not a build
error but a refused task registration at launch.
A team identifier is only needed to run on an iOS device. make app-macos
stays unsigned regardless, so configuring a team does not make local Mac builds
start demanding certificates; use make app-macos-signed when you want one.
| Path | What it is |
|---|---|
Sources/TorrentKit |
Swift API — actors, Sendable models, AsyncStream of events |
Sources/TorrentBridge |
C++17 facade over libtorrent; the only place C++ is touched |
Sources/TorrentFeeds |
RSS/Atom subscriptions and auto-download rules |
Vendor/libtorrent |
submodule pinned to v2.0.14 |
Vendor/boost |
Boost 1.92 headers, fetched by bootstrap.sh, not committed |
Vendor/openssl |
OpenSSL 3.5.8 xcframework, built by build-openssl.sh, not committed |
App/ |
SwiftUI app, shared plus per-platform sources |
App/Resources/Assets.xcassets |
App icon and accent colour, generated by make assets |
Project.swift |
Tuist manifest for the multiplatform app target |
The engine is a plain SwiftPM package at the repository root, so swift build and
swift test work with no Tuist generation step.
Swift cannot catch C++ exceptions. If one reaches Swift the process terminates
with a fatal error and no Swift traceback. Every function in TorrentBridge wraps
its body in try { ... } catch (...) { ... } and reports failure in its return
value. Tests/TorrentKitTests deliberately drives libtorrent into a throw to prove
this still holds.
C++ interop is viral. TorrentKit enables .interoperabilityMode(.Cxx), so
every dependent target must too — including the app, via
SWIFT_OBJC_INTEROP_MODE: objcxx in Project.swift.
- Engine — sessions, adding by
.torrentor magnet, pause/resume/recheck/remove, resume data persisted across launches, clean shutdown. - Management — per-file priorities, peers, trackers, piece availability, per-torrent rate limits, sequential download, queue position, renaming, and moving a torrent's files to another folder.
- Network and privacy — DHT, LSD, UPnP, NAT-PMP, peer exchange, protocol encryption, SOCKS4/5 and HTTP proxies, CIDR address blocking, activity caps.
- Apps — a Mac window with a filter sidebar, sortable table and inspector; an iOS list with swipe actions and a pushed detail view; settings and feeds on both.
- Streaming — a loopback HTTP server with byte-range support that drives libtorrent piece deadlines, so media plays while it downloads, with VLCKit as the player. Sequential download and first/last-piece priority are offered both when a torrent is added and afterwards in its options.
- RSS auto-download — feed subscriptions with include/exclude or regex rules, polled on a schedule, remembering what it has already added.
swift test runs 59 tests with no network. The one that matters most is
LoopbackTransferTests, which runs a seeder and a leecher in the same process,
moves two megabytes between them over loopback, and compares the result
byte-for-byte against the original — so piece transfer and hash verification are
genuinely exercised, not just compiled.
Tests against the public swarm are opt-in, because they depend on trackers and peers being reachable:
TORRENTKIT_LIVE_TESTS=1 swift testOpenSSL 3.5.8 is wired up, so HTTPS trackers and SSL torrents work.
Scripts/build-openssl.sh builds it from source for five architectures and
assembles an xcframework with headers at Headers/openssl, which is what
libtorrent's #include <openssl/ssl.h> needs. The popular prebuilt Swift
packages do not work here: krzyzanowskim/OpenSSL is a 1.3 GB repository and
ships framework-style headers that do not satisfy that include.
A note on the build flags: the script passes no-legacy, which would normally
break BitTorrent's message-stream encryption, since OpenSSL 3 moved RC4 into the
legacy provider. It is safe because libtorrent carries its own RC4 (from
libtomcrypt) and does the Diffie-Hellman exchange with Boost.Multiprecision — it
only needs OpenSSL for TLS, SSL torrents, and hashing.
Skein's own source is under the PolyForm Strict License 1.0.0: build it and run it for noncommercial purposes, but do not redistribute it or distribute modified versions.
That covers this repository's code only. libtorrent, Boost, OpenSSL, VLCKit and FeedKit each carry their own licenses, none of which Skein's license overrides — see THIRD-PARTY-NOTICES.md. They are fetched at build time rather than vendored here, so publishing this repository distributes none of them.
Two things worth knowing:
Publishing a compiled build is a different question. A binary embeds VLCKit, which is LGPL-2.1 and grants recipients rights that a no-redistribution term cannot take away. Source-only publication avoids this entirely, which is why the repository is set up that way. The notices file lists what would have to change first.
A public GitHub repository can be forked. GitHub's Terms of Service §D.5 say that by making a repository public you grant other users a licence to "use, display, perform and reproduce (by forking) Your Content", and your own licence cannot switch that off. If copies existing elsewhere on GitHub is unacceptable, a private repository is the only reliable answer.
Separately: Apple does not allow torrent clients on the App Store, so the iOS target is intended for personal dev-signed builds. macOS distributes normally via Developer ID.