Skip to content

Repository files navigation

Skein

A native BitTorrent client for macOS and iOS, built with SwiftUI over libtorrent using Swift/C++ interop.

Getting started

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 app

make 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.

Bundle identity and signing

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.

Layout

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.

Two constraints worth knowing before editing

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.

What works

  • Engine — sessions, adding by .torrent or 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.

Verifying

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 test

OpenSSL 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.

Licensing

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.

About

Native SwiftUI BitTorrent client for macOS and iOS, built on libtorrent via Swift/C++ interop.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages