mirror of
https://github.com/xweiba/location-spoofer.git
synced 2026-09-21 22:30:46 +08:00
- 坐标转换: detectType 用地理边界判断瓦片类型,加载路径去掉重复 toStored - 图钉: 放 MKMapView 内部 + keyboard 通知双保险,针尖对齐地理中心 - 缩放控件: 转 UIKit 子视图放地图内,键盘弹起一起移动 - 网络监控: WiFi SSID 轮询 + 重连检测,虚拟定位激活时自动验证 - UI: 去飞行模式弹窗和面板,引导页只在首次安装和设置入口触发 - 实时定位: 蓝点数据优先,不限时缓存
153 lines
5.4 KiB
Swift
153 lines
5.4 KiB
Swift
import UIKit
|
|
|
|
@MainActor
|
|
final class ProxyManager: ObservableObject {
|
|
static let shared = ProxyManager()
|
|
|
|
nonisolated let proxyPort = 8888
|
|
|
|
@Published private(set) var isRunning = false
|
|
@Published var error: String?
|
|
|
|
private let certificateStore = CertificateAuthorityStore()
|
|
private var proxyHandle: UInt = 0
|
|
private var coordinateRevision: UInt64 = 0
|
|
|
|
private init() { RuntimeLogger.info("APP", "Proxy", "初始化") }
|
|
|
|
func start() async throws {
|
|
guard !isRunning else { return }
|
|
RuntimeLogger.info("APP", "Proxy.start", "启动代理 127.0.0.1:8888")
|
|
do {
|
|
let authority = try certificateStore.ensure()
|
|
let settings = WlocSettingsStore.load()
|
|
let lat = settings.flatMap { $0.enabled ? $0.latitude : nil } ?? 0
|
|
let lon = settings.flatMap { $0.enabled ? $0.longitude : nil } ?? 0
|
|
let enabled = (settings?.enabled ?? false) ? CInt(1) : CInt(0)
|
|
let accuracy = CInt(settings?.accuracy ?? 25)
|
|
RuntimeLogger.info("APP", "坐标转换", "启动代理: WGS-84", details: [
|
|
"lat": String(lat), "lon": String(lon)
|
|
])
|
|
let result: UInt = authority.certPEM.withCString { cp in
|
|
authority.keyPEM.withCString { kp in
|
|
UInt(wloccore_startproxy(UnsafeMutablePointer(mutating: cp), UnsafeMutablePointer(mutating: kp), CDouble(lat), CDouble(lon), enabled, accuracy))
|
|
}
|
|
}
|
|
guard result != 0 else { CoreBridge.flushLogs(category: "Proxy"); throw ProxyError.startFailed }
|
|
proxyHandle = result
|
|
isRunning = true
|
|
error = nil
|
|
BackgroundKeepAlive.shared.start()
|
|
CoreBridge.flushLogs(category: "Proxy")
|
|
RuntimeLogger.info("APP", "Proxy.start", "启动成功")
|
|
} catch {
|
|
CoreBridge.flushLogs(category: "Proxy")
|
|
RuntimeLogger.error("APP", "Proxy.start", "启动失败", error: error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
func stop() {
|
|
guard isRunning, proxyHandle != 0 else { return }
|
|
_ = wloccore_stopproxy(proxyHandle)
|
|
proxyHandle = 0; isRunning = false; error = nil
|
|
BackgroundKeepAlive.shared.stop()
|
|
CoreBridge.flushLogs(category: "Proxy")
|
|
}
|
|
|
|
@discardableResult
|
|
func setCoords(lat: Double, lon: Double, enabled: Bool, accuracy: Int = 25) -> UInt64 {
|
|
coordinateRevision &+= 1
|
|
wloccore_setcoords(CDouble(lat), CDouble(lon), enabled ? 1 : 0, CInt(accuracy))
|
|
RuntimeLogger.info("APP", "Proxy.coords", "写入坐标", details: [
|
|
"revision": String(coordinateRevision),
|
|
"enabled": String(enabled),
|
|
"lat": String(lat),
|
|
"lon": String(lon)
|
|
])
|
|
return coordinateRevision
|
|
}
|
|
|
|
func coordinateSnapshot(accuracy: Int = 25) -> ProxyCoordinateSnapshot {
|
|
let coordinates = getCoords()
|
|
return ProxyCoordinateSnapshot(
|
|
latitude: coordinates.lat,
|
|
longitude: coordinates.lon,
|
|
enabled: coordinates.enabled,
|
|
accuracy: accuracy,
|
|
revision: coordinateRevision
|
|
)
|
|
}
|
|
|
|
@discardableResult
|
|
func setCoordsIfUnchanged(
|
|
lat: Double,
|
|
lon: Double,
|
|
enabled: Bool,
|
|
accuracy: Int = 25,
|
|
expectedRevision: UInt64
|
|
) -> UInt64? {
|
|
guard coordinateRevision == expectedRevision else {
|
|
RuntimeLogger.info("APP", "Proxy.coords", "跳过过期坐标写入", details: [
|
|
"expectedRevision": String(expectedRevision),
|
|
"currentRevision": String(coordinateRevision)
|
|
])
|
|
return nil
|
|
}
|
|
return setCoords(lat: lat, lon: lon, enabled: enabled, accuracy: accuracy)
|
|
}
|
|
|
|
@discardableResult
|
|
func restoreCoords(_ snapshot: ProxyCoordinateSnapshot, ifUnchangedSince revision: UInt64) -> Bool {
|
|
guard coordinateRevision == revision else {
|
|
RuntimeLogger.info("APP", "Proxy.coords", "跳过旧验证坐标恢复", details: [
|
|
"verificationRevision": String(revision),
|
|
"currentRevision": String(coordinateRevision)
|
|
])
|
|
return false
|
|
}
|
|
setCoords(
|
|
lat: snapshot.latitude,
|
|
lon: snapshot.longitude,
|
|
enabled: snapshot.enabled,
|
|
accuracy: snapshot.accuracy
|
|
)
|
|
return true
|
|
}
|
|
|
|
func getCoords() -> (lat: Double, lon: Double, enabled: Bool) {
|
|
let r = wloccore_getcoords()
|
|
return (Double(r.r0), Double(r.r1), r.r2 != 0)
|
|
}
|
|
|
|
func openCertificateDownload() async {
|
|
do {
|
|
if !isRunning { try await start() }
|
|
// 本地代理直接提供 CA 证书下载
|
|
guard let url = URL(string: "http://127.0.0.1:8888/cert") else { return }
|
|
await MainActor.run { UIApplication.shared.open(url) }
|
|
} catch {
|
|
self.error = "启动代理失败: \(error.localizedDescription)"
|
|
RuntimeLogger.error("APP", "Certificate", "打开证书下载失败", error: error)
|
|
}
|
|
}
|
|
|
|
nonisolated deinit {
|
|
let h = proxyHandle
|
|
if h != 0 { _ = wloccore_stopproxy(h) }
|
|
}
|
|
}
|
|
|
|
struct ProxyCoordinateSnapshot: Equatable {
|
|
let latitude: Double
|
|
let longitude: Double
|
|
let enabled: Bool
|
|
let accuracy: Int
|
|
let revision: UInt64
|
|
}
|
|
|
|
enum ProxyError: LocalizedError {
|
|
case startFailed
|
|
var errorDescription: String? { "Go proxy 启动失败" }
|
|
}
|