Skip to content

swift: add public methods for published states and update docs #37

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,21 @@ actor HelloManager: Dialer {

private func startTailscale() async {
do {
/// This sets up a localAPI client attached to the local node.
// This sets up a localAPI client attached to the local node.
let node = try setupNode()
try await node.up()
let localAPIClient = LocalAPIClient(localNode: node, logger: logger)

// Once we have our local node, we can set up the local API client.
// Create a localAPIClient instance for our local node
let localAPIClient = LocalAPIClient(localNode: node, logger: logger)
setLocalAPIClient(localAPIClient)
setReady(true)

/// This sets up a bus watcher to listen for changes in the netmap. These will be sent to the given consumer, uin
/// this case, a HelloModel which will keep track of the changes and publish them.
if let processor = await localAPIClient.watchIPNBus(mask: [.initialState, .netmap, .rateLimitNetmaps, .noPrivateKeys],
consumer: model) {
setProcessor(processor)
}
// This sets up a bus watcher to listen for changes in the netmap. These will be sent to the given consumer, in
// this case, a HelloModel which will keep track of the changes and publish them.
let busEventMask: Ipn.NotifyWatchOpt = [.initialState, .netmap, .rateLimitNetmaps, .noPrivateKeys]
let processor = try await localAPIClient.watchIPNBus(mask: busEventMask ,
consumer: model)
setProcessor(processor)
setReady(true)
} catch {
Logger().log("Error setting up Tailscale: \(error)")
setReady(false)
Expand Down Expand Up @@ -102,7 +102,7 @@ actor HelloManager: Dialer {
return
}

await setMessage("Phoning " + Settings.tailnetServer + "...")
await setMessage("Phoning " + Settings.tailnetURL + "...")

// Create a URLSession that can access nodes on the tailnet.
// .tailscaleSession(node) is the magic sauce. This sends your URLRequest via
Expand All @@ -111,12 +111,11 @@ actor HelloManager: Dialer {
let session = URLSession(configuration: sessionConfig)

// Request a resource from the tailnet...
let url = URL(string: Settings.tailnetServer)!
var req = URLRequest(url: url)

let url = URL(string: Settings.tailnetURL)!
let req = URLRequest(url: url)

let (data, _) = try await session.data(for: req)
await setMessage("\(Settings.tailnetServer) says:\n \(String(data: data, encoding: .utf8) ?? "(crickets!)")")
await setMessage("\(Settings.tailnetURL) says:\n \(String(data: data, encoding: .utf8) ?? "(crickets!)")")
} catch {
await setMessage("Whoops!: \(error)")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ struct Settings {
static let authKey = "tskey-auth-your-auth-key"
// Note: The sample has a transport exception for http on ts.net so http:// is ok...
// The "Phone Home" button will load the contents of this URL, it should be on your Tailnet.
static let tailnetServer = "http://myserver.my-tailnet.ts.net"
static let tailnetURL = "http://myserver.my-tailnet.ts.net"
// Identifies this application in the Tailscale admin console.
static let hostName = "Hello-From-Tailsacle-Sample-App"
}
Expand Down
9 changes: 9 additions & 0 deletions swift/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ endif
# The xcodebuild schemes will run the Makefile in the root directory to build
# the libtailscale.a and libtailscale_ios.a dependencies.

.PHONY: all
all: test ios macos ## Runs the tests and builds all library targets

.PHONY: macos
macos: ## Builds TailscaleKit for macos to swift/build/Build/Products/Release (unsigned)
@echo
@echo "::: Building TailscaleKit for macOS :::"
cd .. && make c-archive
mkdir -p build
xcodebuild build -scheme "TailscaleKit (macOS)" \
Expand All @@ -22,6 +27,8 @@ macos: ## Builds TailscaleKit for macos to swift/build/Build/Products/Release (

.PHONY: ios
ios: ## Builds TailscaleKit for iOS to swift/build/Build/Products/Release (unsigned)
@echo
@echo "::: Building TailscaleKit for iOS :::"
cd .. && make c-archive-ios
mkdir -p build
xcodebuild build -scheme "TailscaleKit (iOS)" \
Expand All @@ -33,6 +40,8 @@ ios: ## Builds TailscaleKit for iOS to swift/build/Build/Products/Release (unsi

.PHONY: test
test: ## Run tests (macOS)
@echo
@echo "::: Running tests for TailscaleKit :::"
cd .. && make c-archive
mkdir -p build
xcodebuild test -scheme TailscaleKitXCTests \
Expand Down
76 changes: 76 additions & 0 deletions swift/TailscaleKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,13 @@
buildSettings = {
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CLANG_WARN_FRAMEWORK_INCLUDE_PRIVATE_FROM_PUBLIC = YES;
CLANG_WARN_IMPLICIT_FALLTHROUGH = YES;
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES_ERROR;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES_ERROR;
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
Expand All @@ -656,6 +663,13 @@
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_MODULE_VERIFIER = YES;
FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/..";
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_PEDANTIC = NO;
GCC_WARN_SHADOW = YES;
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GENERATE_INFOPLIST_FILE = YES;
HEADER_SEARCH_PATHS = (
"$(SRCROOT)/..",
Expand All @@ -681,11 +695,13 @@
PRODUCT_MODULE_NAME = TailscaleKit;
PRODUCT_NAME = TailscaleKit;
PROVISIONING_PROFILE_SPECIFIER = "";
RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = macosx;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = macosx;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_INSTALL_OBJC_HEADER = NO;
SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
SWIFT_VERSION = 6.0;
TARGETED_DEVICE_FAMILY = "1,2,7";
XROS_DEPLOYMENT_TARGET = 2.1;
Expand All @@ -697,6 +713,13 @@
buildSettings = {
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CLANG_WARN_FRAMEWORK_INCLUDE_PRIVATE_FROM_PUBLIC = YES;
CLANG_WARN_IMPLICIT_FALLTHROUGH = YES;
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES_ERROR;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES_ERROR;
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
Expand All @@ -706,6 +729,13 @@
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_MODULE_VERIFIER = YES;
FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/..";
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_PEDANTIC = NO;
GCC_WARN_SHADOW = YES;
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GENERATE_INFOPLIST_FILE = YES;
HEADER_SEARCH_PATHS = (
"$(SRCROOT)/..",
Expand All @@ -731,11 +761,13 @@
PRODUCT_MODULE_NAME = TailscaleKit;
PRODUCT_NAME = TailscaleKit;
PROVISIONING_PROFILE_SPECIFIER = "";
RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = macosx;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = macosx;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_INSTALL_OBJC_HEADER = NO;
SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
SWIFT_VERSION = 6.0;
TARGETED_DEVICE_FAMILY = "1,2,7";
XROS_DEPLOYMENT_TARGET = 2.1;
Expand All @@ -753,15 +785,20 @@
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_ASSIGN_ENUM = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_COMPLETION_HANDLER_MISUSE = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_FRAMEWORK_INCLUDE_PRIVATE_FROM_PUBLIC = YES;
CLANG_WARN_IMPLICIT_FALLTHROUGH = YES;
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES_ERROR;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
Expand All @@ -770,7 +807,9 @@
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
Expand All @@ -789,16 +828,31 @@
"DEBUG=1",
"$(inherited)",
);
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
GCC_WARN_PEDANTIC = YES;
GCC_WARN_SHADOW = YES;
GCC_WARN_SIGN_COMPARE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNKNOWN_PRAGMAS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
RUN_CLANG_STATIC_ANALYZER = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
VERSIONING_SYSTEM = "apple-generic";
Expand All @@ -817,15 +871,20 @@
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_ASSIGN_ENUM = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_COMPLETION_HANDLER_MISUSE = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_FRAMEWORK_INCLUDE_PRIVATE_FROM_PUBLIC = YES;
CLANG_WARN_IMPLICIT_FALLTHROUGH = YES;
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES_ERROR;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
Expand All @@ -834,7 +893,9 @@
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
Expand All @@ -847,15 +908,30 @@
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_NO_COMMON_BLOCKS = YES;
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
GCC_WARN_PEDANTIC = YES;
GCC_WARN_SHADOW = YES;
GCC_WARN_SIGN_COMPARE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNKNOWN_PRAGMAS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
RUN_CLANG_STATIC_ANALYZER = YES;
SWIFT_COMPILATION_MODE = wholemodule;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
Expand Down
17 changes: 12 additions & 5 deletions swift/TailscaleKit/IncomingConnection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,19 @@ public actor IncomingConnection {

public let remoteAddress: String?

@Published public var state: ConnectionState = .idle
@Published var _state: ConnectionState = .idle

public func state() -> any AsyncSequence<ConnectionState, Never> {
$_state
.removeDuplicates()
.eraseToAnyPublisher()
.values
}

init(conn: TailscaleConnection, remoteAddress: String?, logger: LogSink? = nil) async {
self.logger = logger
self.conn = conn
self.state = .connected
_state = .connected
self.remoteAddress = remoteAddress
reader = SocketReader(conn: conn)
}
Expand All @@ -35,13 +42,13 @@ public actor IncomingConnection {
unistd.close(conn)
conn = 0
}
state = .closed
_state = .closed
}

/// Returns up to size bytes from the connection. Blocks until
/// data is available
public func receive(maximumLength: Int = 4096, timeout: Int32) async throws -> Data {
guard state == .connected else {
guard _state == .connected else {
throw TailscaleError.connectionClosed
}

Expand All @@ -50,7 +57,7 @@ public actor IncomingConnection {

/// Reads a complete message from the connection
public func receiveMessage( timeout: Int32) async throws -> Data {
guard state == .connected else {
guard _state == .connected else {
throw TailscaleError.connectionClosed
}

Expand Down
15 changes: 11 additions & 4 deletions swift/TailscaleKit/Listener.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@ public actor Listener {

private let logger: LogSink?

@Published public var state: ListenterState = .idle
@Published var _state: ListenerState = .idle

public func state() -> any AsyncSequence<ListenerState, Never> {
$_state
.removeDuplicates()
.eraseToAnyPublisher()
.values
}

/// Initializes and readies a new listener
///
Expand All @@ -34,13 +41,13 @@ public actor Listener {
let res = tailscale_listen(tailscale, proto.rawValue, address, &listener)

guard res == 0 else {
state = .failed
_state = .failed
let msg = tailscale.getErrorMessage()
let err = TailscaleError.fromPosixErrCode(res, msg)
logger?.log("Listener failed to initialize: \(msg) (\(err.localizedDescription))")
throw err
}
state = .listening
_state = .listening
}

deinit {
Expand All @@ -56,7 +63,7 @@ public actor Listener {
unistd.close(listener)
listener = 0
}
state = .closed
_state = .closed
}

/// Blocks and awaits a new incoming connection
Expand Down
Loading
Loading