# bark-apns
Direct APNs client for sending [Bark](https://github.com/Finb/Bark) notifications.
This crate talks to APNs directly with an iOS device token, bypassing the Bark
server entirely. Messages can also be sent with Bark-compatible encryption, so
notification content stays private while delivery remains fast and direct.
## Key Features
- **✨ First-class `serde` payloads**: Uses strongly typed payload models and `serde` serialization throughout, avoiding brittle hand-built JSON.
- **🛡️ Correct Bark encryption semantics**: Validates algorithm, mode, key, and IV up front, supports all Bark AES options, and serializes encrypted pushes exactly as Bark expects.
- **🚀 Lean, direct APNs delivery**: Sends straight to APNs with no third-party relay or self-hosted backend in the path, keeping delivery fast, simple, and private.
## Usage
### Send a simple message
```rust
use bark_apns::{Bark, Message};
fn main() -> bark_apns::Result<()> {
let bark = Bark::new()?;
let message = Message::new()
.title("Deploy")
.body("done")
.group("ops");
bark.send(&message, ["device-token-from-bark-app"])?;
Ok(())
}
```
### Send a markdown message
```rust
use bark_apns::{Bark, Message};
fn main() -> bark_apns::Result<()> {
let bark = Bark::new()?;
let message = Message::new()
.title("Deploy")
.markdown("## Deploy\n\n- status: **done**\n- target: `production`");
bark.send(&message, ["device-token-from-bark-app"])?;
Ok(())
}
```
> [!NOTE]
> Bark parses Markdown with Apple's Swift Markdown package and its own renderer.
> It supports paragraphs, headings, block quotes, bold, italic, strikethrough,
> inline code, code blocks, links, images, ordered and unordered lists, nested
> lists, task-list checkboxes, soft breaks, and hard line breaks. In notification
> banners, Bark uses the rendered plain-text body and collapses repeated blank
> lines, so styling such as bold, italic, link color, and code font is not
> preserved there.
### Send an encrypted message
```rust
use bark_apns::{Bark, Encryption, EncryptionAlgorithm, EncryptionMode, Message};
fn main() -> bark_apns::Result<()> {
let bark = Bark::new()?;
let encryption = Encryption::new(
EncryptionAlgorithm::AES128,
EncryptionMode::CBC,
"1234567890123456",
)?;
let message = Message::new()
.title("Deploy")
.body("done")
.markdown("**Deploy** finished")
.group("ops")
.encryption(encryption);
bark.send(&message, ["device-token-from-bark-app"])?;
Ok(())
}
```
The payload is built with `serde`, not string concatenation. Encrypted pushes place
Bark request fields in the encrypted `ciphertext` JSON and leave only `ciphertext`
and `iv` in APNs custom user info.
## Error Handling
Library APIs return `bark_apns::Result<T>`, a typed `Result<T, bark_apns::Error>`.
Applications can still use `anyhow::Result` at their own boundary:
```rust
fn run() -> anyhow::Result<()> {
bark_apns::Bark::new()?;
Ok(())
}
```
## License
This project is licensed under the [MIT license](LICENSE).