Files
libtailscale/swift/README.md
T
Jonathan Nobels 40f559c067 Makefile/swift: update Makefile and add support for iOS
updates tailscale/tailscale#13937

Adds a Makefile for building various targets.  Adds support for building
iOS compatible libtailscale_ios.a.   Removes SOCKS5 support for iOS where
it looks like the CFNetwork support is lacking.

Fixes a few unexported symbols in TailscaleKit.

Added macOS sample app.
2025-03-24 14:01:54 -04:00

3.1 KiB

TailscaleKit

The TailscaleKit Swift package provides an embedded network interface that can be used to listen for and dial connections to other Tailscale nodes in addition to an extension to URLSession which allows you to make URL requests to nodes on you Tailnet directly.

The interfaces are similar in design to NWConnection, but are Swift 6 compliant and designed to be used in modern async/await style code.

Build and Install

Build Requirements:

  • XCode 16.1 or newer

Building Tailscale.framework:

First build the libtailscale dependecies:

From /swift

$ make macos
# make ios

Will build TailscaleKit.framework into /swift/build/Build/Products.

Separate frameworks will be built for macOS and iOS. All dependencies (libtailscale.a) are built automatically. Swift 6 is supported.

Alternatively, you may build from xCode using the Tailscale scheme but the libraries must be built first (since xCode will complain about paths and permissions)

From /

$ make c-archive
$ make c-archive-ios 

Non-apple builds are not supported (yet). We do use URLSession and Combine though it is possible to purge both.

Tests

From /swift

$ make test

Usage

Nodes need to be authorized in order to function. Set an auth key via the config.authKey parameter, or watch the log stream and respond to the printed authorization URL.

Here's a working example using an auth key:


// Configures a Tailscale node and starts it up.  The node here (and the key we would use to
// authenticate it) are marked as 'ephemeral' - meaning that the node will be disposed of as
// soon as it goes offline.
func start() -> TailscaleNode {
    let dataDir = getDocumentDirectoryPath().absoluteString + "tailscale"
    let authKey = "tsnet-auth-put-your-auth-key-key-here"
    let config = Configuration(hostName: "TSNet-Test",
                               path: dataDir,
                               authKey: authKey,
                               controlURL: Configuration.defaultControlURL,
                               ephemeral: true)

    // The logger is configurable.  The default will just print.
    let node = try TailscaleNode(config: config, logger: DefaultLogger())
    
    // Bring the node up 
    try await node.up()
    return node
}

// Do a URL request via the loopback proxy
// Where url is a node on your tailnet such as https://server.fiesty-pangolin.ts.net/thing
func fetchURL(_ url: URL, tailscale: TailscaleNode) async throws -> Data {
    // You can cache this.  It will not change once the node is up.
    let sessionConfig = try await URLSessionConfiguration.tailscaleSession(tailscale)
    let session = URLSession(configuration: sessionConfig)
    
    // Make the request
    let req = URLRequest(url: url)
    let (data, _) = try await session.data(for: req)
    return data
}

See the TailscaleKitTests for more examples.

Contributing

Pull requests are welcome on GitHub at https://github.com/tailscale/libtailscale

Please file any issues about this code or the hosted service on the issue tracker.