SwiftSFTP is a modern, ergonomic Swift Package Manager library that wraps libssh2 and OpenSSL. It provides a high-level async/await API for seamless SFTP file transfers and SSH-based interactions, while also exposing the underlying C APIs for advanced use cases.
Whether you need to quickly upload files, manage a remote filesystem, or build anything on top of SSH, SwiftSFTP offers a robust and asynchronous interface built on industry-standard libraries.
Features:
- Ergonomic API: High-level abstractions over SFTP and SSH functionalities using modern Swift concurrency (
async/await). - Solid Base: Built on top of the robust and reliable
libssh2andOpenSSL(vendored; dynamic frameworks on Apple platforms). - Fast: Outperforms other Swift SFTP clients, with multi-worker transfers further increasing throughput; see Benchmarks.
- Resumable Transfers:
upload/downloadandmultiUpload/multiDownloadcan continue interrupted transfers; see Uploading and Downloading and Resumable parallel transfers. - Shell Agent: Server-side copy, move, hash, archive, download and related work over a persistent shell on the same session, without hauling the payload over the network; see Shell Agent.
- Cryptographic Utilities: OpenSSL helpers to validate user keys and
known_hostshost keys, plus key generation and conversion for Ed25519, Ed448, ECDSA, RSA, ML-DSA and SLH-DSA; see Cryptographic Utilities. - Low-Level Access: Fully exposed
libssh2wrappers (Layer 0), allowing users to expand functionality or perform other non-SFTP related SSH tasks.
Here is a quick-start example showing how to connect, navigate, and upload files using SwiftSFTP:
import SwiftSFTP
import Foundation
// - Instantiate an SFTP client
let myClient = try SFTPClient(
openSocketIn: .init(hostname: "localhost", port: 6922),
hostKeyAcceptance: .acceptAny, // Configurable for strict host key checking
authentication: .init(name: "bulbasaur", auth: .password("pass123")) // Also supports .privateKey
)
// - Call this to connect
try await myClient.login()
// - Check server banner
let banner = try await myClient.banner
print(banner)
// - Get the home directory
let home = try await myClient.currentWorkingDirectory
print(home)
// - List a directory
let contents = try await myClient.listDirectory(path: "./Fixtures")
// - Open a file handle
if let largestFile = contents.bySize.last {
let myFileHandle = try await myClient.openFile([.read], path: largestFile.fullPath)
// Read first bytes
let firstBytes = try await myFileHandle.read(upTo: 1024)
// Seek
myFileHandle.offset = 2048
// ...
try await myFileHandle.close()
}
// - Upload a file with progress tracking
let localFile = URL(fileURLWithPath: "myfile.zip")
try await myClient.upload(from: localFile, to: "/home/bulbasaur/myfolder/myfile.zip")
{ doneBytes, totalBytes, lastBlockBytes, lastBlockTime in
// Return true/false to continue/terminate the upload
let progress = ((Double(doneBytes) / Double(totalBytes)) * 100).rounded()
let speed = (Double(lastBlockBytes) / Double(lastBlockTime)).rounded()
print("Completed \(progress)% at \(speed) B/s")
return true
}
// - Delete a file or directory with everything in it (less radical methods available)
try await myClient.delete(path: "myfolder")
// - Close the connection
try await myClient.close()A complete user guide can be found here.
For offline key validation, host key checks, and asymmetric key generation and conversion, see Cryptographic Utilities.
Add the following dependency to your Package.swift file:
dependencies: [
.package(url: "https://github.com/RuiNelson/SwiftSFTP.git", from: "3.0.0")
]Then, add SwiftSFTP to the dependencies of your target:
targets: [
.target(
name: "YourTargetName",
dependencies: [
.product(name: "SwiftSFTP", package: "SwiftSFTP"),
]
)
]If you are using Xcode, you can directly add this repository as a Swift Package dependency in your project settings:
- Navigate to File > Add Package Dependencies...
- Enter the repository URL:
https://github.com/RuiNelson/SwiftSFTP.git - Choose the version rule you prefer (e.g., "Up to Next Major Version") and click Add Package.
- Make sure the
SwiftSFTPproduct is added to your app target.
The SwiftSFTP product is dynamically linked. On Apple platforms this keeps its
Swift and libssh2 OpenSSL calls bound to OpenSSLCrypto.framework when another
SDK statically links a crypto implementation with the same C symbol names.
The application must embed the dynamic SwiftSFTP and OpenSSL frameworks;
Xcode handles this for normal SwiftPM product dependencies.
See packaging regression tests for a standalone
consumer test and the optional official MEGA artifact check.
Measured with the Benchmark executable against a real world Wi-Fi connected SFTP server and client (Client ↔ Wi-Fi AP ↔ Server), comparing SwiftSFTP to Citadel 0.12.1, another Swift library. Each figure is the best of 3 runs. The test file is 100 MiB in size and contains random data.
With a single connection, SwiftSFTP and Citadel upload at a comparable rate, but SwiftSFTP is already 42% faster at download (10.23 vs. 7.21 MiB/s) before any additional workers are involved.
That single-connection download gap comes down to how each client issues read requests. Citadel's SFTP client sends one read request and awaits its reply before sending the next, so its throughput is limited by how much data the server returns per round trip. libssh2 (the C library SwiftSFTP is built on) reads ahead instead: it keeps several read requests outstanding at once rather than waiting on each one, so more data stays in flight on the same connection. Over a real network, where every round trip has a cost, that difference alone accounts for SwiftSFTP's download lead before multiDownload is even used.
SwiftSFTP's advantage grows further when transferring with multiple workers (multiUpload / multiDownload), which distribute a transfer across several independent TCP connections rather than one.
A single TCP connection's throughput is bounded by its own congestion window: the sender grows it gradually and retreats at the first sign of loss or congestion, which limits how much data one connection can keep in flight at a time, particularly over Wi-Fi, where variable latency and loss trigger that retreat frequently. SFTP's 32 KiB per-packet limit compounds this: libssh2 pipelines writes ahead of the server's acknowledgment, but each outstanding unit is still only 32 KiB, so filling the same congestion window over a higher-latency link takes many more packets than it would with a larger block size. Each multiUpload / multiDownload worker opens its own TCP connection (with its own congestion window and its own stream of 32 KiB packets), so the transfer as a whole keeps far more data in flight than a single connection allows. This is why five workers more than double upload throughput above (4.98 → 11.69 MiB/s) and two workers produce a smaller gain on download (10.23 → 13.22 MiB/s), which was already closer to what a single connection could sustain on this link.
Resuming is close to free. A resumable upload matched the plain one exactly (11.69 MiB/s in both), and a resumable download gave up about 3% (13.22 → 12.78 MiB/s).
This throughput is not free:
- Additional server load. Each worker holds its own connection and session open for the duration of the transfer, which some servers rate-limit or cap.
- Diminishing, link-dependent returns. The gain from additional workers depends on the link and on the remote server's capacity to service concurrent requests, and will vary across networks; it should not be assumed from the figures above.
Because that last point makes the right worker count a property of the link rather than a constant, multiTune measures it: it transfers a test file at increasing parallelism and returns the count that reached the highest throughput. The Benchmark package ships it as the swift-sftp-multitune executable, and the User's Guide covers the API.
SwiftSFTP is available under the Apache License 2.0.
SwiftSFTP depends on the following libraries. Their licenses apply when you use or redistribute SwiftSFTP (including redistribution of the vendored OpenSSL XCFrameworks).
| Component | Role | License | Source |
|---|---|---|---|
| SwiftSFTP | This package | Apache License 2.0 | LICENSE |
| libssh2 | SSH/SFTP protocol (vendored C sources) | BSD-3-Clause | vendor/libssh2/COPYING |
| OpenSSL | Cryptography (vendored; XCFrameworks on Apple platforms, system OpenSSL on Linux/Android) | Apache License 2.0 | vendor/openssl/LICENSE.txt |
| PathWorks | Path utilities (SwiftPM dependency) | MIT | PathWorks |
| swift-log | Logging (SwiftPM dependency) | Apache License 2.0 | apple/swift-log |
All of the licenses above are permissive. They allow use of SwiftSFTP and its dependencies in proprietary, commercial, closed-source applications (including apps distributed through the App Store and other app stores), without requiring you to open-source your own code.
In practice, that means you may:
- Link SwiftSFTP (and the bundled libssh2 / OpenSSL) into closed-source products
- Sell or distribute those products without publishing your application source
- Modify SwiftSFTP or its dependencies for your own use (subject to each license’s terms)
You remain responsible for meeting each license’s attribution and notice requirements. Typical obligations when you distribute a binary that includes this software:
- Apache 2.0 (SwiftSFTP, OpenSSL, swift-log): include a copy of the license; retain copyright and attribution notices; if you modify the licensed work, state that you changed it; if a
NOTICEfile is present, include its attribution notices as required by the license - BSD-3-Clause (libssh2): retain the copyright notice, conditions, and disclaimer in source redistributions; reproduce them in documentation and/or other materials provided with binary redistributions; do not use the copyright holders’ names to endorse your product without permission
- MIT (PathWorks): include the copyright notice and permission notice in all copies or substantial portions of the software
A common way to satisfy these is an About, Acknowledgments, or Open Source Licenses screen (or a licenses file shipped with the product) that lists the components above and includes the corresponding license texts.
None of these licenses is copyleft (unlike the GPL family): linking against them does not force your application’s source code to be released under an open-source license.
This section is a practical summary, not legal advice. For compliance questions specific to your product or jurisdiction, consult a lawyer and the full license texts linked above.
