Release 3.8.2.13: iOS Packet Tunnel client and Clash URL subscriptions.
ci / test (macos-latest) (push) Waiting to run
ci / test (ubuntu-latest) (push) Waiting to run
ci / test (windows-latest) (push) Waiting to run

Add SwiftUI + Network Extension (LibXray/hev), parse Clash Meta YAML from subscription URLs into vless/trojan/hy2 share links, keep the selected server on connect, and ship Navis-3.8.2.13.ipa with versioned changelog.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
M4
2026-08-01 22:46:32 +03:00
co-authored by Cursor
parent 8b564110d5
commit 7ceb3a5148
44 changed files with 4165 additions and 72 deletions
+90
View File
@@ -0,0 +1,90 @@
import Foundation
enum Proto: String, Codable, Sendable {
case vless
case vmess
case trojan
case hy2
case naive
case awg
case unknown
var label: String { rawValue }
static func detect(_ uri: String) -> Proto {
let lower = uri.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
if lower.hasPrefix("vless://") { return .vless }
if lower.hasPrefix("vmess://") { return .vmess }
if lower.hasPrefix("trojan://") { return .trojan }
if lower.hasPrefix("hysteria2://") || lower.hasPrefix("hy2://") { return .hy2 }
if lower.hasPrefix("naive+") || lower.hasPrefix("naive://") { return .naive }
// Desktop DetectProtocol: bare http(s)/quic with userinfo naive
if lower.hasPrefix("https://") || lower.hasPrefix("http://") || lower.hasPrefix("quic://") {
return .naive
}
if lower.hasPrefix("awg://") || lower.hasPrefix("amneziawg://") || lower.hasPrefix("wireguard://") {
return .awg
}
if lower.localizedCaseInsensitiveContains("[Interface]") && lower.contains("Jc") {
return .awg
}
return .unknown
}
/// Protocols with a working in-process core in the current iOS appex.
var supportedOnIOS: Bool {
switch self {
case .vless, .vmess, .trojan:
return true
case .hy2:
// YAML builder ready; runtime needs unified NavisCore (LibXray Go runtime conflict).
return false
case .naive, .awg, .unknown:
return false
}
}
var iosUnsupportedReason: String? {
switch self {
case .hy2:
return "Hysteria2: пока используйте VLESS/VMess/Trojan (единый NavisCore для Hy2 — следующий шаг)"
case .naive:
return "NaiveProxy на iOS недоступен (Chromium-ядро)"
case .awg:
return "AmneziaWG на iOS — следующим этапом"
case .unknown:
return "Неизвестный протокол"
default:
return nil
}
}
}
struct Profile: Identifiable, Codable, Equatable, Sendable {
var id: String
var name: String
var uri: String
var host: String
var protocolKind: Proto { Proto.detect(uri) }
init(id: String, name: String, uri: String, host: String = "") {
self.id = id
self.name = name
self.uri = uri
self.host = host.isEmpty ? LinkParser.hostOf(uri) : host
}
}
struct PingResult: Identifiable, Sendable {
var id: String
var ms: Int64 = -1
var ok: Bool = false
var error: String?
}
struct TunnelConfig: Codable, Sendable {
var name: String
var uri: String
var startedAt: Double
}