Skip to content
Quickstarts

Apple quickstart

Add the ForestVPN Swift package from source and read your first tunnel status on iOS, macOS, or tvOS.

The Apple SDK is a source SwiftPM package at forestvpn-apple/ in the ForestVPN repository. Its Swift API (ForestVPNKit) wraps the Rust control-plane runtime through a UniFFI layer (ForestVPNFFI), so one build of the native library serves your app and its Network Extension.

Honest distribution note: there is no prebuilt xcframework download and no registry package — you add the package from a source checkout and build the Rust FFI once yourself. Platform floors: iOS 18.0, macOS 14.0, tvOS 17.0.

Prerequisites

  • macOS with Xcode (the toolchain that ships swift 6+)
  • The Rust toolchain (rustup.rs) — the FFI library builds from Rust source
  • A checkout of the ForestVPN repository

1. Get the source

git clone [email protected]:forestvpn/forestvpn.git
cd forestvpn

2. Build the Rust FFI once

The Swift package links a native library built from the Rust workspace. From the repository root:

make apple-ffi-xcframework

This produces forestvpn-apple/Frameworks/ForestvpnApple.xcframework — the single native artifact your app target and a Network Extension appex both link. The Swift package itself links the library by name (libforestvpn_apple_ffi), so make sure the xcframework (or, for command-line swift build/swift test, a -Xlinker -L<path> pointing at the built library) is on your target's link path.

3. Add the package

In Xcode: File → Add Package Dependencies… → Add Local… and select the forestvpn-apple/ directory, then add the step-2 xcframework to your target's Frameworks, Libraries, and Embedded Content. Or, from your own Package.swift, add a path dependency:

Package.swift (yours)
dependencies: [
  .package(path: "../forestvpn/forestvpn-apple"),
],
targets: [
  .target(
    name: "MyApp",
    dependencies: [
      .product(name: "ForestVPNKit", package: "forestvpn-apple"),
    ]
  ),
]

ForestVPNKit is the app-facing product; the package also exposes ForestVPNFFI (the raw UniFFI bindings) and further products for Network Extension, billing, and the generated API clients.

4. First call: open a session, read status

FirstStatus.swift
import Foundation
import ForestVPNKit

/// Open a control-plane session and read the current status.
///
/// The bootstrap profile JSON comes from your project's consumer plane
/// (`GET /consumer/v1/bootstrap`) after the device authenticates.
func firstStatus(bootstrapProfileJSON: String, stateDirectory: URL) throws -> Data {
  print("SDK ABI \(ForestVPNControlPlaneClient.abiVersion), "
    + "runtime \(ForestVPNControlPlaneClient.runtimeVersion)")

  let client = try ForestVPNControlPlaneClient.open(
    stateDirectory: stateDirectory,
    bootstrapProfileJSON: bootstrapProfileJSON
  )
  defer { client.close() }

  return try client.statusJSON()
}

The bootstrap profile JSON is the per-device configuration your backend fetches from GET /consumer/v1/bootstrap after the device authenticates — see Devices for the device lifecycle.

5. Run

Build your app target as usual (swift build inside a package that depends on ForestVPNKit, or ⌘R in Xcode). On first launch the snippet prints the SDK ABI and runtime versions, then the current status document as JSON.

Verified by

scripts/quickstart-verify/apple-swiftpm.sh — resolves the package manifest, asserts the documented products exist, and parses this page's snippet on every CI run (the full swift build needs the step-2 Rust build, so CI on non-macOS runners skips it loudly).