Files
archived-libtailscale/swift/Examples/TailscaleKitHello/HelloFromTailscale/HelloViewModel.swift
Jonathan Nobels d5a3c8e8ef swift, go.mod: adding localAPI support via SOCK5
updates tailscale/tailscale#13937

This adds localAPI support into TailscaleKit.  LocalAPI can now be queried
via the SOCK5 proxy on both MacOS and iOS.   This also fixes SOCKS5
support for iOS so you can simply apply our config to a URLSession.

This pulls in most of LocalAPI - though much of it is untested, it's based
on the implementation in tailscale/corp/xcode.

Unit tests pending.

Signed-off-by: Jonathan Nobels <jonathan@tailscale.com>
2025-04-25 11:55:09 -04:00

92 lines
2.3 KiB
Swift

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
import SwiftUI
@preconcurrency import Combine
import TailscaleKit
@Observable
class HelloViewModel: @unchecked Sendable {
var message: String = "Ready to phone home!"
var peerCountMessage = "Waiting for peers...."
var stateMessage = "Waiting for state...."
var modelObservers = [Task<Void, Never>]()
@MainActor
init(model: HelloModel) {
bindToModel(model)
}
deinit {
modelObservers.forEach { $0.cancel() }
}
@MainActor
func handleStateChange(_ state: Ipn.State?) {
guard let state else {
self.stateMessage = "Waiting for state...."
return
}
self.stateMessage = "IPNState: \(state)"
}
@MainActor
func handlePeersChange(_ peers: [Tailcfg.Node]?) {
guard let peers else {
self.peerCountMessage = "Waiting for peers..."
return
}
if peers.count > 0 {
self.peerCountMessage = "Found \(peers.count) peers"
} else {
self.peerCountMessage = "No peers found"
}
}
@MainActor
func bindToModel(_ model: HelloModel) {
modelObservers.forEach { $0.cancel() }
modelObservers.removeAll()
Task {
await handleStateChange(model.state)
await handlePeersChange(model.peers)
}
modelObservers.append( Task { [weak self] in
for await peers in await model.peersStream {
if Task.isCancelled { return }
guard let self = self else { return }
await MainActor.run { handlePeersChange(peers) }
}
})
modelObservers.append( Task { [weak self] in
for await state in await model.stateStream {
if Task.isCancelled { return }
guard let self = self else { return }
await MainActor.run { handleStateChange(state) }
}
})
}
@MainActor
func setMessage(_ message: String) {
self.message = message
}
func runRequest(_ dialer: Dialer) {
Task {
let model = self
await dialer.phoneHome { msg in
await MainActor.run {
model.setMessage(msg)
}
}
}
}
}