# Swift-Sodium: libsodium with a Swift face on Apple platforms

> Swift-Sodium wraps the Sodium library for macOS, iOS, tvOS, watchOS and Linux, ships a prebuilt XCFramework so nothing has to be compiled on the developer's machine, and exposes secret key, AEAD and public key operations through one Swift API.

**jedisct1/swift-sodium** — Safe and easy to use crypto for iOS and macOS

- Repository: https://github.com/jedisct1/swift-sodium
- Stars: 556 · Forks: 208
- Language: C
- License: ISC
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/jedisct1-swift-sodium

## What it wraps and where it runs

Swift-Sodium provides a safe and easy to use interface to perform common cryptographic operations on Apple platforms, which are macOS, iOS, tvOS and watchOS, and on Linux. It leverages the Sodium library, and although Swift is the primary target, the framework can also be used in Objective-C applications. That last point is the one that matters for adoption, because a lot of existing Apple platform code is Objective-C, and a binding that only works from Swift is a binding you cannot use from half your codebase. The project is ISC licensed, and the last push to the repository was on 2026-05-04, with the release 0.11.0 published the same day after 0.10.0 in January 2026.

## Adding it to a project, and the prebuilt C library

Adding Swift-Sodium to an Xcode project means going to File, then Swift Packages, then Add Package Dependency, entering the repository URL, and importing both Sodium and Clibsodium. The C library itself does not have to be installed on the system, because the repository already includes a precompiled library for armv7, armv7s and arm64, as well as for the iOS simulator, WatchOS and Catalyst, packaged as the Clibsodium.xcframework framework. That prebuilt framework was generated by the apple-xcframework.sh script from the libsodium repository, and the documentation is specific about reproducibility, stating that running the script on Xcode 26.4 at the given libsodium revision produces files identical to the ones present in the repository. The import in your own source is a single line.

```swift
import Sodium
```

## Secret key cryptography and the three ways to get a key

Messages are encrypted and decrypted using the same secret key, which is the definition of symmetric cryptography, and a key can be obtained in three ways. It can be generated with the key() method, it can be derived from a password using the Password Hashing API, or it can be computed from a secret key and the peer's public key using the Key Exchange API. The structure of the Swift API mirrors the C library closely, with the sodium instance holding a namespace per primitive, so a secret key operation lives under sodium.secretBox and a key pair under sodium.box. That naming discipline is what keeps the binding predictable as you move between primitives, and it is also what makes the API documentation a substitute for the libsodium documentation, since the concepts map one to one.

## Secret streams, and why they are the interesting API

The secret stream API is the part of the library that is more than a thin wrapper. A stream is a sequence of messages that are encrypted as they depart and decrypted as they arrive, and the encrypted messages are expected to be received in the same order as they were sent. Streams can be arbitrarily long, so the API can be used for file encryption by splitting the file into small chunks so the whole file does not need to be in memory at once, and it can also be used to exchange a sequence of messages between two peers. The decryption function automatically checks that the chunks have been received without modification, and against truncation or reordering, and a tag is attached to each message so it can signal the end of a sub-sequence with PUSH or the end of the whole string with FINAL.

```swift
let sodium = Sodium()
let message1 = "Message 1".bytes
let message2 = "Message 2".bytes
let message3 = "Message 3".bytes

let secretkey = sodium.secretStream.xchacha20poly1305.key()

/* stream encryption */

let stream_enc = sodium.secretStream.xchacha20poly1305.initPush(secretKey: secretkey)!
let header = stream_enc.header()
let encrypted1 = stream_enc.push(message: message1)!
let encrypted2 = stream_enc.push(message: message2)!
let encrypted3 = stream_enc.push(message: message3, tag: .FINAL)!

/* stream decryption */

let stream_dec = sodium.secretStream.xchacha20poly1305.initPull(secretKey: secretkey, header: header)!
let (message1_dec, tag1) = stream_dec.pull(cipherText: encrypted1)!
let (message2_dec, tag2) = stream_dec.pull(cipherText: encrypted2)!
let (message3_dec, tag3) = stream_dec.pull(cipherText: encrypted3)!
```

The header is what makes the scheme work in practice, because it is what the receiving side needs to derive the same stream state.

## Single message encryption, and what it does not give you

For a single message there is the SecretBox, which encrypts with a key and opens it again, and the decryption process checks that the messages have not been tampered with before decrypting them.

```swift
let sodium = Sodium()
let message = "My Test Message".bytes
let secretKey = sodium.secretBox.key()
let encrypted: Bytes = sodium.secretBox.seal(message: message, secretKey: secretKey)!
if let decrypted = sodium.secretBox.open(nonceAndAuthenticatedCipherText: encrypted, secretKey: secretKey) {
    // authenticator is valid, decrypted contains the original message
}
```

The limitation is stated in the documentation and is worth internalising. Messages encrypted this way are independent, so if multiple messages are sent this way the recipient cannot detect if some messages have been duplicated, deleted or reordered without the sender including additional data with each message. If you need ordering guarantees, that is what the secret stream is for, and if you need the sender to prove who sent it, that is what the box API is for.

## AEAD, and the four constructions on offer

AEAD ciphers encrypt and authenticate a message and also authenticate optional non-confidential data, so the ciphertext cannot be decrypted without the key, and tampering with either the ciphertext or the additional data is detected. Four constructions are available. xchacha20poly1305ietf is XChaCha20-Poly1305 and is recommended for most applications. aegis128l is AEGIS-128L, described as very fast on modern CPUs with AES acceleration. aegis256 is AEGIS-256, similar to AEGIS-128L but with a 256-bit key. aes256gcm is AES256-GCM, for hardware-accelerated AES on Intel and ARM.

```swift
let sodium = Sodium()
let message = "My secret message".bytes
let additionalData = "v1".bytes

let key = sodium.aead.xchacha20poly1305ietf.key()
let encrypted: Bytes = sodium.aead.xchacha20poly1305ietf.encrypt(
    message: message,
    secretKey: key,
    additionalData: additionalData)!

let decrypted = sodium.aead.xchacha20poly1305ietf.decrypt(
    nonceAndAuthenticatedCipherText: encrypted,
    secretKey: key,
    additionalData: additionalData)
```

The additional data is the useful part of AEAD, because it lets you bind a version string or a record identifier to the ciphertext, so an attacker cannot replay a valid message under a different context.

## Public key cryptography and the box API

With public key cryptography, each peer has two keys: a secret key that has to remain secret, and a public key that anyone can use to send an encrypted message to that peer, and that public key can only be used to encrypt, while the corresponding secret is required to decrypt.

```swift
let sodium = Sodium()
let aliceKeyPair = sodium.box.keyPair()!
let bobKeyPair = sodium.box.keyPair()!
let message = "My Test Message".bytes

let encryptedMessageFromAliceToBob: Bytes =
    sodium.box.seal(message: message,
                    recipientPublicKey: bobKeyPair.publicKey,
                    senderSecretKey: aliceKeyPair.secretKey)!

let messageVerifiedAndDecryptedByBob =
    sodium.box.open(nonceAndAuthenticatedCipherText: encryptedMessageFromAliceToBob,
                    senderPublicKey: aliceKeyPair.publicKey,
                    recipientSecretKey: bobKeyPair.secretKey)
```

Two details matter. The recipient has to know the sender's public key as well, and will reject a message that does not appear to be valid for the expected public key, which is what turns encryption into authentication. And seal() automatically generates a nonce and prepends it to the ciphertext while open() extracts the nonce and decrypts, so the usual nonce-misuse bug is not one you can write with this API, though the Box class also provides a variant that takes a user-defined nonce if you need one.

## Where the library stops and the platform starts

The boundary is clear and worth naming. This is a binding over libsodium, so the set of primitives available is the set libsodium offers, and if you need something it does not have, this library will not invent it. The examples directory shows the platform coverage, with separate projects for iOS, macOS, tvOS, Watch and the watch extension, and a CommonCode directory shared between them, which is a practical illustration of the Objective-C and Swift compatibility claim. Two operational details deserve a note. The prebuilt Clibsodium framework is a snapshot of a particular libsodium revision, so if you need a newer C library you are rebuilding the XCFramework yourself, and the documentation gives the exact upstream revision that reproduces the committed files. And the release history has a gap worth noticing, with 0.9.1 from December 2020 followed by 0.10.0 in January 2026 and 0.11.0 in May 2026, so the recent work is clustered at the end rather than spread out.

## Conclusion

Swift-Sodium fits a Swift developer on Apple platforms who needs well-understood symmetric and public key primitives without writing their own C interop, since the API mirrors libsodium's own vocabulary and the C library is prebuilt, so there is no toolchain setup step. It does not fit a project that needs primitives libsodium does not have, since this is a binding and not a toolkit, and it does not fit a team that needs to pin the underlying C library version independently, because the prebuilt XCFramework is what you get by default. Before you adopt it, check which architectures the bundled Clibsodium covers, since the repository ships builds for armv7, armv7s and arm64 plus the iOS simulator, WatchOS and Catalyst, and read the note about the upstream libsodium revision that produced the framework if you intend to rebuild it yourself.

## FAQ

### How do I add Swift-Sodium to an Xcode project?

Select File, then Swift Packages, then Add Package Dependency, enter the repository URL, and import both Sodium and Clibsodium. The Sodium library does not have to be installed on the system, because the repository includes a precompiled Clibsodium library for armv7, armv7s, arm64, the iOS simulator, WatchOS and Catalyst.

### What is the difference between the secret stream and the secret box in Swift-Sodium?

A secret stream encrypts a sequence of messages that must arrive in order, and its decryption automatically checks that chunks were not modified, truncated or reordered, which is what makes it suitable for file encryption in chunks. A secret box encrypts single independent messages, so the recipient cannot detect duplicated, deleted or reordered messages without extra data from the sender.

### Which AEAD constructions does Swift-Sodium expose?

Four: xchacha20poly1305ietf, which is XChaCha20-Poly1305 and is recommended for most applications, aegis128l for AEGIS-128L with AES acceleration, aegis256 for AEGIS-256 with a 256-bit key, and aes256gcm for hardware-accelerated AES on Intel and ARM.

### Can I use Swift-Sodium from Objective-C?

Yes. The project states that although Swift is the primary target, the framework can also be used in Objective-C applications, and the examples directory includes a CommonCode directory shared between the platform projects alongside the iOS, macOS, tvOS, Watch and watch extension ones.

## Sources

- [Issues](https://github.com/jedisct1/swift-sodium/issues)
- [jedisct1/swift-sodium on GitHub](https://github.com/jedisct1/swift-sodium)
- [License: ISC](https://github.com/jedisct1/swift-sodium/blob/master/LICENSE)
- [README](https://github.com/jedisct1/swift-sodium/blob/master/README.md)
- [Releases](https://github.com/jedisct1/swift-sodium/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jedisct1-swift-sodium
