初始化信息

This commit is contained in:
2025-10-13 18:08:02 +08:00
parent e302ebe176
commit 35a90f1635
616 changed files with 58357 additions and 25 deletions
Executable
+34
View File
@@ -0,0 +1,34 @@
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3
+10
View File
@@ -0,0 +1,10 @@
//
// Base.xcconfig
// Runner
//
// Created by GFWFighter on 7/24/1402 AP.
//
BASE_BUNDLE_IDENTIFIER=app.baer.com
SERVICE_IDENTIFIER=com.baer.app
DEVELOPMENT_TEAM=3UR892FAP3
+26
View File
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>15.0</string>
</dict>
</plist>
+4
View File
@@ -0,0 +1,4 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
#include "Base.xcconfig"
+4
View File
@@ -0,0 +1,4 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
#include "Base.xcconfig"
View File
+26
View File
@@ -0,0 +1,26 @@
// swift-tools-version: 5.4
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "Hiddify Packages",
platforms: [
// Minimum platform version
.iOS(.v13)
],
products: [
.library(
name: "Libcore",
targets: ["Libcore"]),
],
dependencies: [
// No dependencies
],
targets: [
.binaryTarget(
name: "Libcore",
path: "../Frameworks/Libcore.xcframework"
)
]
)
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>packet-tunnel-provider</string>
</array>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.$(BASE_BUNDLE_IDENTIFIER)</string>
</array>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>
+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BASE_BUNDLE_IDENTIFIER</key>
<string>$(BASE_BUNDLE_IDENTIFIER)</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.networkextension.packet-tunnel</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
</dict>
</dict>
</plist>
+52
View File
@@ -0,0 +1,52 @@
//
// Logger.swift
// SingBoxPacketTunnel
//
// Created by GFWFighter on 10/24/23.
//
import Foundation
class Logger {
private static let queue = DispatchQueue.init(label: "\(FilePath.packageName).PacketTunnelLog", qos: .utility)
private let fileManager = FileManager.default
private let url: URL
private var _fileHandle: FileHandle?
private var fileHandle: FileHandle? {
get {
if let _fileHandle { return _fileHandle }
let handle = try? FileHandle(forWritingTo: url)
_fileHandle = handle
return handle
}
}
private var lock = NSLock()
init(path: URL) {
url = path
}
func write(_ message: String) {
Logger.queue.async { [message, unowned self] () in
lock.lock()
defer { lock.unlock() }
let output = message + "\n"
do {
if !self.fileManager.fileExists(atPath: url.path) {
try output.write(to: url, atomically: true, encoding: .utf8)
} else {
guard let fileHandle else {
return
}
fileHandle.seekToEndOfFile()
if let data = output.data(using: .utf8) {
fileHandle.write(data)
}
}
} catch {}
}
}
}
+37
View File
@@ -0,0 +1,37 @@
//
// PacketTunnelProvider.swift
// SingBoxPacketTunnel
//
// Created by GFWFighter on 7/24/1402 AP.
//
import NetworkExtension
class PacketTunnelProvider: ExtensionProvider {
private var upload: Int64 = 0
private var download: Int64 = 0
// private var trafficLock: NSLock = NSLock()
// var trafficReader: TrafficReader!
override func startTunnel(options: [String : NSObject]?) async throws {
try await super.startTunnel(options: options)
/*trafficReader = TrafficReader { [unowned self] traffic in
trafficLock.lock()
upload += traffic.up
download += traffic.down
trafficLock.unlock()
}*/
}
override func handleAppMessage(_ messageData: Data) async -> Data? {
let message = String(data: messageData, encoding: .utf8)
switch message {
case "stats":
return "\(upload),\(download)".data(using: .utf8)!
default:
return nil
}
}
}
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>packet-tunnel-provider</string>
</array>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.app.baer.com</string>
</array>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>
+25
View File
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>C617.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
</array>
</dict>
</plist>
+43
View File
@@ -0,0 +1,43 @@
//
// Extension+RunBlocking.swift
// SingBoxPacketTunnel
//
// Created by GFWFighter on 7/25/1402 AP.
//
import Foundation
import Libcore
import NetworkExtension
func runBlocking<T>(_ block: @escaping () async -> T) -> T {
let semaphore = DispatchSemaphore(value: 0)
let box = resultBox<T>()
Task.detached {
let value = await block()
box.result0 = value
semaphore.signal()
}
semaphore.wait()
return box.result0
}
func runBlocking<T>(_ tBlock: @escaping () async throws -> T) throws -> T {
let semaphore = DispatchSemaphore(value: 0)
let box = resultBox<T>()
Task.detached {
do {
let value = try await tBlock()
box.result = .success(value)
} catch {
box.result = .failure(error)
}
semaphore.signal()
}
semaphore.wait()
return try box.result.get()
}
private class resultBox<T> {
var result: Result<T, Error>!
var result0: T!
}
+240
View File
@@ -0,0 +1,240 @@
//
// ExtensionPlatformInterface.swift
// SingBoxPacketTunnel
//
// Created by GFWFighter on 7/25/1402 AP.
//
import Foundation
import Libcore
import NetworkExtension
public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtocol, LibboxCommandServerHandlerProtocol {
public func readWIFIState() -> LibboxWIFIState? {
return nil;
}
private let tunnel: ExtensionProvider
private var networkSettings: NEPacketTunnelNetworkSettings?
init(_ tunnel: ExtensionProvider) {
self.tunnel = tunnel
}
public func openTun(_ options: LibboxTunOptionsProtocol?, ret0_: UnsafeMutablePointer<Int32>?) throws {
try runBlocking { [self] in
try await openTun0(options, ret0_)
}
}
private func openTun0(_ options: LibboxTunOptionsProtocol?, _ ret0_: UnsafeMutablePointer<Int32>?) async throws {
guard let options else {
throw NSError(domain: "nil options", code: 0)
}
guard let ret0_ else {
throw NSError(domain: "nil return pointer", code: 0)
}
let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1")
if options.getAutoRoute() {
settings.mtu = NSNumber(value: options.getMTU())
var error: NSError?
let dnsServer = options.getDNSServerAddress(&error)
if let error {
throw error
}
settings.dnsSettings = NEDNSSettings(servers: [dnsServer])
var ipv4Address: [String] = []
var ipv4Mask: [String] = []
let ipv4AddressIterator = options.getInet4Address()!
while ipv4AddressIterator.hasNext() {
let ipv4Prefix = ipv4AddressIterator.next()!
ipv4Address.append(ipv4Prefix.address())
ipv4Mask.append(ipv4Prefix.mask())
}
let ipv4Settings = NEIPv4Settings(addresses: ipv4Address, subnetMasks: ipv4Mask)
var ipv4Routes: [NEIPv4Route] = []
let inet4RouteAddressIterator = options.getInet4RouteAddress()!
if inet4RouteAddressIterator.hasNext() {
while inet4RouteAddressIterator.hasNext() {
let ipv4RoutePrefix = inet4RouteAddressIterator.next()!
ipv4Routes.append(NEIPv4Route(destinationAddress: ipv4RoutePrefix.address(), subnetMask: ipv4RoutePrefix.mask()))
}
} else {
ipv4Routes.append(NEIPv4Route.default())
}
for (index, address) in ipv4Address.enumerated() {
ipv4Routes.append(NEIPv4Route(destinationAddress: address, subnetMask: ipv4Mask[index]))
}
ipv4Settings.includedRoutes = ipv4Routes
settings.ipv4Settings = ipv4Settings
var ipv6Address: [String] = []
var ipv6Prefixes: [NSNumber] = []
let ipv6AddressIterator = options.getInet6Address()!
while ipv6AddressIterator.hasNext() {
let ipv6Prefix = ipv6AddressIterator.next()!
ipv6Address.append(ipv6Prefix.address())
ipv6Prefixes.append(NSNumber(value: ipv6Prefix.prefix()))
}
let ipv6Settings = NEIPv6Settings(addresses: ipv6Address, networkPrefixLengths: ipv6Prefixes)
var ipv6Routes: [NEIPv6Route] = []
let inet6RouteAddressIterator = options.getInet6RouteAddress()!
if inet6RouteAddressIterator.hasNext() {
while inet6RouteAddressIterator.hasNext() {
let ipv6RoutePrefix = inet4RouteAddressIterator.next()!
ipv6Routes.append(NEIPv6Route(destinationAddress: ipv6RoutePrefix.description, networkPrefixLength: NSNumber(value: ipv6RoutePrefix.prefix())))
}
} else {
ipv6Routes.append(NEIPv6Route.default())
}
ipv6Settings.includedRoutes = ipv6Routes
settings.ipv6Settings = ipv6Settings
}
if options.isHTTPProxyEnabled() {
let proxySettings = NEProxySettings()
let proxyServer = NEProxyServer(address: options.getHTTPProxyServer(), port: Int(options.getHTTPProxyServerPort()))
proxySettings.httpServer = proxyServer
proxySettings.httpsServer = proxyServer
settings.proxySettings = proxySettings
}
networkSettings = settings
try await tunnel.setTunnelNetworkSettings(settings)
if let tunFd = tunnel.packetFlow.value(forKeyPath: "socket.fileDescriptor") as? Int32 {
ret0_.pointee = tunFd
return
}
let tunFdFromLoop = LibboxGetTunnelFileDescriptor()
if tunFdFromLoop != -1 {
ret0_.pointee = tunFdFromLoop
} else {
throw NSError(domain: "missing file descriptor", code: 0)
}
}
public func usePlatformAutoDetectControl() -> Bool {
true
}
public func autoDetectControl(_: Int32) throws {}
public func findConnectionOwner(_: Int32, sourceAddress _: String?, sourcePort _: Int32, destinationAddress _: String?, destinationPort _: Int32, ret0_ _: UnsafeMutablePointer<Int32>?) throws {
throw NSError(domain: "not implemented", code: 0)
}
public func packageName(byUid _: Int32, error _: NSErrorPointer) -> String {
""
}
public func uid(byPackageName _: String?, ret0_ _: UnsafeMutablePointer<Int32>?) throws {
throw NSError(domain: "not implemented", code: 0)
}
public func useProcFS() -> Bool {
false
}
public func writeLog(_ message: String?) {
guard let message else {
return
}
tunnel.writeMessage(message)
}
public func usePlatformDefaultInterfaceMonitor() -> Bool {
false
}
public func startDefaultInterfaceMonitor(_: LibboxInterfaceUpdateListenerProtocol?) throws {}
public func closeDefaultInterfaceMonitor(_: LibboxInterfaceUpdateListenerProtocol?) throws {}
public func useGetter() -> Bool {
false
}
public func getInterfaces() throws -> LibboxNetworkInterfaceIteratorProtocol {
throw NSError(domain: "not implemented", code: 0)
}
public func underNetworkExtension() -> Bool {
true
}
public func includeAllNetworks() -> Bool {
#if !os(tvOS)
// return SharedPreferences.includeAllNetworks.getBlocking()
return false
#else
return false
#endif
}
public func clearDNSCache() {
guard let networkSettings else {
return
}
tunnel.reasserting = true
tunnel.setTunnelNetworkSettings(nil) { _ in
}
tunnel.setTunnelNetworkSettings(networkSettings) { _ in
}
tunnel.reasserting = false
}
public func serviceReload() throws {
runBlocking { [self] in
await tunnel.reloadService()
}
}
public func getSystemProxyStatus() -> LibboxSystemProxyStatus? {
let status = LibboxSystemProxyStatus()
guard let networkSettings else {
return status
}
guard let proxySettings = networkSettings.proxySettings else {
return status
}
if proxySettings.httpServer == nil {
return status
}
status.available = true
status.enabled = proxySettings.httpEnabled
return status
}
public func setSystemProxyEnabled(_ isEnabled: Bool) throws {
guard let networkSettings else {
return
}
guard let proxySettings = networkSettings.proxySettings else {
return
}
if proxySettings.httpServer == nil {
return
}
if proxySettings.httpEnabled == isEnabled {
return
}
proxySettings.httpEnabled = isEnabled
proxySettings.httpsEnabled = isEnabled
networkSettings.proxySettings = proxySettings
try runBlocking {
try await self.tunnel.setTunnelNetworkSettings(networkSettings)
}
}
public func postServiceClose() {
// TODO
}
func reset() {
networkSettings = nil
}
}
+167
View File
@@ -0,0 +1,167 @@
//
// ExtensionProvider.swift
// SingBoxPacketTunnel
//
// Created by GFWFighter on 7/25/1402 AP.
//
import Foundation
import Libcore
import NetworkExtension
open class ExtensionProvider: NEPacketTunnelProvider {
public static let errorFile = FilePath.workingDirectory.appendingPathComponent("network_extension_error")
private var commandServer: LibboxCommandServer!
private var boxService: LibboxBoxService!
private var systemProxyAvailable = false
private var systemProxyEnabled = false
private var platformInterface: ExtensionPlatformInterface!
private var config: String!
override open func startTunnel(options: [String: NSObject]?) async throws {
try? FileManager.default.removeItem(at: ExtensionProvider.errorFile)
try? FileManager.default.removeItem(at: FilePath.workingDirectory.appendingPathComponent("TestLog"))
let disableMemoryLimit = (options?["DisableMemoryLimit"] as? NSString as? String ?? "NO") == "YES"
guard let config = options?["Config"] as? NSString as? String else {
writeFatalError("(packet-tunnel) error: config not provided")
return
}
guard let config = SingBox.setupConfig(config: config) else {
writeFatalError("(packet-tunnel) error: config is invalid")
return
}
self.config = config
do {
try FileManager.default.createDirectory(at: FilePath.workingDirectory, withIntermediateDirectories: true)
} catch {
writeFatalError("(packet-tunnel) error: create working directory: \(error.localizedDescription)")
return
}
LibboxSetup(
FilePath.sharedDirectory.relativePath,
FilePath.workingDirectory.relativePath,
FilePath.cacheDirectory.relativePath,
false
)
var error: NSError?
LibboxRedirectStderr(FilePath.cacheDirectory.appendingPathComponent("stderr.log").relativePath, &error)
if let error {
writeError("(packet-tunnel) redirect stderr error: \(error.localizedDescription)")
}
LibboxSetMemoryLimit(!disableMemoryLimit)
if platformInterface == nil {
platformInterface = ExtensionPlatformInterface(self)
}
commandServer = LibboxNewCommandServer(platformInterface, Int32(30))
do {
try commandServer.start()
} catch {
writeFatalError("(packet-tunnel): log server start error: \(error.localizedDescription)")
return
}
writeMessage("(packet-tunnel) log server started")
await startService()
}
func writeMessage(_ message: String) {
if let commandServer {
commandServer.writeMessage(message)
} else {
NSLog(message)
}
}
func writeError(_ message: String) {
writeMessage(message)
try? message.write(to: ExtensionProvider.errorFile, atomically: true, encoding: .utf8)
}
public func writeFatalError(_ message: String) {
#if DEBUG
NSLog(message)
#endif
writeError(message)
cancelTunnelWithError(NSError(domain: message, code: 0))
}
private func startService() async {
let configContent = config
var error: NSError?
let service = LibboxNewService(configContent, platformInterface, &error)
if let error {
writeError("(packet-tunnel) error: create service: \(error.localizedDescription)")
return
}
guard let service else {
return
}
do {
try service.start()
} catch {
writeError("(packet-tunnel) error: start service: \(error.localizedDescription)")
return
}
boxService = service
commandServer.setService(service)
}
private func stopService() {
if let service = boxService {
do {
try service.close()
} catch {
writeError("(packet-tunnel) error: stop service: \(error.localizedDescription)")
}
boxService = nil
commandServer.setService(nil)
}
if let platformInterface {
platformInterface.reset()
}
}
func reloadService() async {
writeMessage("(packet-tunnel) reloading service")
reasserting = true
defer {
reasserting = false
}
stopService()
await startService()
}
override open func stopTunnel(with reason: NEProviderStopReason) async {
writeMessage("(packet-tunnel) stopping, reason: \(reason)")
stopService()
if let server = commandServer {
try? await Task.sleep(nanoseconds: 100 * NSEC_PER_MSEC)
try? server.close()
commandServer = nil
}
}
override open func handleAppMessage(_ messageData: Data) async -> Data? {
messageData
}
override open func sleep() async {
if let boxService {
boxService.pause()
}
}
override open func wake() {
if let boxService {
boxService.wake()
}
}
}
+60
View File
@@ -0,0 +1,60 @@
//
// SingBox.swift
// SingBoxPacketTunnel
//
// Created by GFWFighter on 7/25/1402 AP.
//
import Foundation
class SingBox {
static func setupConfig(config: String, mtu: Int = 9000) -> String? {
guard
let config = config.data(using: .utf8),
var json = try? JSONSerialization
.jsonObject(
with: config,
options: [.mutableLeaves, .mutableContainers]
) as? [String:Any]
else {
return nil
}
/*json["log"] = [
"disabled": false,
"level": "info",
"output": "log",
"timestamp": true
] as [String:Any]
json["experimental"] = [
"clash_api": [
"external_controller": "127.0.0.1:10864"
]
]
json["inbounds"] = [
[
"type": "tun",
"inet4_address": "172.19.0.1/30",
"auto_route": true,
"mtu": mtu,
"sniff": true
] as [String:Any]
]
var routing = (json["route"] as? [String:Any]) ?? [
"rules": [Any](),
"auto_detect_interface": true,
"final": (json["inbounds"] as? [[String:Any]])?.first?["tag"] ?? "proxy"
]
routing["geoip"] = [
"path": FilePath.assetsDirectory.appendingPathComponent("geoip.db"),
]
routing["geosite"] = [
"path": FilePath.assetsDirectory.appendingPathComponent("geosite.db"),
]
json["route"] = routing*/
guard let data = try? JSONSerialization.data(withJSONObject: json) else {
return nil
}
return String(data: data, encoding: .utf8)
}
}
+70
View File
@@ -0,0 +1,70 @@
//
// TrafficReader.swift
// SingBoxPacketTunnel
//
// Created by GFWFighter on 7/25/1402 AP.
//
import Foundation
struct TrafficReaderUpdate: Codable {
let up: Int64
let down: Int64
}
class TrafficReader {
private var task: URLSessionWebSocketTask!
private let callback: (TrafficReaderUpdate) -> ()
init(onUpdate: @escaping (TrafficReaderUpdate) -> ()) {
self.callback = onUpdate
Task(priority: .background) { [weak self] () in
await self?.setup()
}
}
private func setup() async {
try? await Task.sleep(nanoseconds: 5_000_000_000)
//return
while true {
do {
let (_, response) = try await URLSession.shared.data(from: URL(string: "http://127.0.0.1:10864")!)
let code = (response as? HTTPURLResponse)?.statusCode ?? -1
if code >= 200 && code < 300 {
break
}
} catch {
// pass
}
try? await Task.sleep(nanoseconds: 5_000_000)
}
let task = URLSession.shared.webSocketTask(with: URL(string: "ws://127.0.0.1:10864/traffic")!)
self.task = task
read()
task.resume()
}
private func read() {
task.receive { [weak self] result in
switch result {
case .failure(_):
break
case .success(let message):
switch message {
case .string(let message):
guard let data = message.data(using: .utf8) else {
break
}
guard let response = try? JSONDecoder().decode(TrafficReaderUpdate.self, from: data) else {
break
}
self?.callback(response)
default:
break
}
self?.read()
}
}
}
}
Executable
+51
View File
@@ -0,0 +1,51 @@
source 'https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git'
# Uncomment this line to define a global platform for your project
platform :ios, '15.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
pod 'EasyPermissionX/Camera'
target 'RunnerTests' do
inherit! :search_paths
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'
end
end
end
+75
View File
@@ -0,0 +1,75 @@
PODS:
- connectivity_plus (0.0.1):
- Flutter
- ReachabilitySwift
- EasyPermissionX/Camera (0.0.2)
- Flutter (1.0.0)
- flutter_udid (0.0.1):
- Flutter
- SAMKeychain
- package_info_plus (0.4.5):
- Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- permission_handler_apple (9.3.0):
- Flutter
- ReachabilitySwift (5.2.4)
- SAMKeychain (1.5.3)
- url_launcher_ios (0.0.1):
- Flutter
- webview_flutter_wkwebview (0.0.1):
- Flutter
- FlutterMacOS
DEPENDENCIES:
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- EasyPermissionX/Camera
- Flutter (from `Flutter`)
- flutter_udid (from `.symlinks/plugins/flutter_udid/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`)
SPEC REPOS:
https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git:
- EasyPermissionX
- ReachabilitySwift
- SAMKeychain
EXTERNAL SOURCES:
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
Flutter:
:path: Flutter
flutter_udid:
:path: ".symlinks/plugins/flutter_udid/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
webview_flutter_wkwebview:
:path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"
SPEC CHECKSUMS:
connectivity_plus: 481668c94744c30c53b8895afb39159d1e619bdf
EasyPermissionX: ff4c438f6ee80488f873b4cb921e32d982523067
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_udid: f7c3884e6ec2951efe4f9de082257fc77c4d15e9
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
ReachabilitySwift: 32793e867593cfc1177f5d16491e3a197d2fccda
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
webview_flutter_wkwebview: 1821ceac936eba6f7984d89a9f3bcb4dea99ebb2
PODFILE CHECKSUM: 579a354deb8d6fdc55c12799569018594328642e
COCOAPODS: 1.16.2
+1380
View File
File diff suppressed because it is too large Load Diff
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1540"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "03E392B52ADDA00E000ADF15"
BuildableName = "PacketTunnel.appex"
BlueprintName = "PacketTunnel"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Release"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
askForAppToLaunch = "Yes"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
askForAppToLaunch = "Yes"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
+101
View File
@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C8080294A63A400263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
+35
View File
@@ -0,0 +1,35 @@
import UIKit
import Flutter
import Libcore
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
setupFileManager()
registerHandlers()
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
func setupFileManager() {
try? FileManager.default.createDirectory(at: FilePath.workingDirectory, withIntermediateDirectories: true)
FileManager.default.changeCurrentDirectoryPath(FilePath.sharedDirectory.path)
}
func registerHandlers() {
KRMethodHandler.register(with: self.registrar(forPlugin: KRMethodHandler.name)!)
KRPlatformMethodHandler.register(with: self.registrar(forPlugin: KRPlatformMethodHandler.name)!)
KRFileMethodHandler.register(with: self.registrar(forPlugin: KRFileMethodHandler.name)!)
KRStatusEventHandler.register(with: self.registrar(forPlugin: KRStatusEventHandler.name)!)
KRAlertsEventHandler.register(with: self.registrar(forPlugin: KRAlertsEventHandler.name)!)
KRLogsEventHandler.register(with: self.registrar(forPlugin: KRLogsEventHandler.name)!)
KRGroupsEventHandler.register(with: self.registrar(forPlugin: KRGroupsEventHandler.name)!)
KRActiveGroupsEventHandler.register(with: self.registrar(forPlugin: KRActiveGroupsEventHandler.name)!)
KRStatsEventHandler.register(with: self.registrar(forPlugin: KRStatsEventHandler.name)!)
}
}
+120
View File
@@ -0,0 +1,120 @@
{
"images": [
{
"size": "20x20",
"idiom": "universal",
"filename": "icon-20@2x.png",
"scale": "2x",
"platform": "ios"
},
{
"size": "20x20",
"idiom": "universal",
"filename": "icon-20@3x.png",
"scale": "3x",
"platform": "ios"
},
{
"size": "29x29",
"idiom": "universal",
"filename": "icon-29@2x.png",
"scale": "2x",
"platform": "ios"
},
{
"size": "29x29",
"idiom": "universal",
"filename": "icon-29@3x.png",
"scale": "3x",
"platform": "ios"
},
{
"size": "38x38",
"idiom": "universal",
"filename": "icon-38@2x.png",
"scale": "2x",
"platform": "ios"
},
{
"size": "38x38",
"idiom": "universal",
"filename": "icon-38@3x.png",
"scale": "3x",
"platform": "ios"
},
{
"size": "40x40",
"idiom": "universal",
"filename": "icon-40@2x.png",
"scale": "2x",
"platform": "ios"
},
{
"size": "40x40",
"idiom": "universal",
"filename": "icon-40@3x.png",
"scale": "3x",
"platform": "ios"
},
{
"size": "60x60",
"idiom": "universal",
"filename": "icon-60@2x.png",
"scale": "2x",
"platform": "ios"
},
{
"size": "60x60",
"idiom": "universal",
"filename": "icon-60@3x.png",
"scale": "3x",
"platform": "ios"
},
{
"size": "64x64",
"idiom": "universal",
"filename": "icon-64@2x.png",
"scale": "2x",
"platform": "ios"
},
{
"size": "64x64",
"idiom": "universal",
"filename": "icon-64@3x.png",
"scale": "3x",
"platform": "ios"
},
{
"size": "68x68",
"idiom": "universal",
"filename": "icon-68@2x.png",
"scale": "2x",
"platform": "ios"
},
{
"size": "76x76",
"idiom": "universal",
"filename": "icon-76@2x.png",
"scale": "2x",
"platform": "ios"
},
{
"size": "83.5x83.5",
"idiom": "universal",
"filename": "icon-83.5@2x.png",
"scale": "2x",
"platform": "ios"
},
{
"size": "1024x1024",
"idiom": "universal",
"filename": "icon-1024.png",
"scale": "1x",
"platform": "ios"
}
],
"info": {
"version": 1,
"author": "icon.wuruihong.com"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

+6
View File
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "background.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 69 B

+24
View File
@@ -0,0 +1,24 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "icon-83.5@2x.png",
"scale" : "1x"
},
{
"filename" : "icon-83.5@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "icon-83.5@2x.png",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
+5
View File
@@ -0,0 +1,5 @@
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

+44
View File
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="LaunchBackground" translatesAutoresizingMaskIntoConstraints="NO" id="tWc-Dq-wcI"/>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"></imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="3T2-ad-Qdv"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="RPx-PI-7Xg"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="SdS-ul-q2q"/>
<constraint firstAttribute="trailing" secondItem="tWc-Dq-wcI" secondAttribute="trailing" id="Swv-Gf-Rwn"/>
<constraint firstAttribute="trailing" secondItem="YRO-k0-Ey4" secondAttribute="trailing" id="TQA-XW-tRk"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="duK-uY-Gun"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="kV7-tw-vXt"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="xPn-NY-SIU"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="1024" height="1024"/>
<image name="LaunchBackground" width="1" height="1"/>
</resources>
</document>
+29
View File
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22154" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22130"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-26" y="-77"/>
</scene>
</scenes>
</document>
+18
View File
@@ -0,0 +1,18 @@
//
// Bundle+Properties.swift
// Runner
//
// Created by Hiddify on 12/26/23.
//
import Foundation
extension Bundle {
var serviceIdentifier: String {
(infoDictionary?["SERVICE_IDENTIFIER"] as? String)!
}
var baseBundleIdentifier: String {
(infoDictionary?["BASE_BUNDLE_IDENTIFIER"] as? String)!
}
}
+50
View File
@@ -0,0 +1,50 @@
import Foundation
import Combine
import Libcore
public class KRActiveGroupsEventHandler: NSObject, FlutterPlugin, FlutterStreamHandler {
static let name = "\(Bundle.main.serviceIdentifier)/active-groups"
private var commandClient: CommandClient?
private var channel: FlutterEventChannel?
private var events: FlutterEventSink?
private var cancellable: AnyCancellable?
public static func register(with registrar: FlutterPluginRegistrar) {
let instance = KRActiveGroupsEventHandler()
instance.channel = FlutterEventChannel(name: Self.name,
binaryMessenger: registrar.messenger())
instance.channel?.setStreamHandler(instance)
}
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
FileManager.default.changeCurrentDirectoryPath(FilePath.sharedDirectory.path)
self.events = events
commandClient = CommandClient(.groupsInfoOnly)
commandClient?.connect()
cancellable = commandClient?.$groups.sink{ [self] sbGroups in
self.kr_writeGroups(sbGroups)
}
return nil
}
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
commandClient?.disconnect()
cancellable?.cancel()
events = nil
return nil
}
func kr_writeGroups(_ sbGroups: [SBGroup]?) {
guard let sbGroups else {return}
if
let groups = try? JSONEncoder().encode(sbGroups),
let groups = String(data: groups, encoding: .utf8)
{
DispatchQueue.main.async { [events = self.events, groups] () in
events?(groups)
}
}
}
}
+45
View File
@@ -0,0 +1,45 @@
//
// AlertEventHandler.swift
// Runner
//
// Created by GFWFighter on 10/24/23.
//
import Foundation
import Combine
public class KRAlertsEventHandler: NSObject, FlutterPlugin, FlutterStreamHandler {
static let name = "\(Bundle.main.serviceIdentifier)/service.alerts"
private var channel: FlutterEventChannel?
private var cancellable: AnyCancellable?
public static func register(with registrar: FlutterPluginRegistrar) {
let instance = KRAlertsEventHandler()
instance.channel = FlutterEventChannel(name: Self.name, binaryMessenger: registrar.messenger(), codec: FlutterJSONMethodCodec())
instance.channel?.setStreamHandler(instance)
}
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
cancellable = VPNManager.shared.$alert.sink { [events] alert in
var data = [
"status": "Stopped",
"alert": alert.alert?.rawValue,
"message": alert.message,
]
for key in data.keys {
if data[key] == nil {
data.removeValue(forKey: key)
}
}
events(data)
}
return nil
}
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
cancellable?.cancel()
return nil
}
}
+40
View File
@@ -0,0 +1,40 @@
//
// FileMethodHandler.swift
// Runner
//
// Created by GFWFighter on 10/23/23.
//
import Flutter
import Combine
import Libcore
public class KRFileMethodHandler: NSObject, FlutterPlugin {
public static let name = "\(Bundle.main.serviceIdentifier)/file"
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: Self.name, binaryMessenger: registrar.messenger())
let instance = KRFileMethodHandler()
registrar.addMethodCallDelegate(instance, channel: channel)
instance.channel = channel
}
private var channel: FlutterMethodChannel?
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "get_paths":
result(kr_getPaths(args: call.arguments))
default:
result(FlutterMethodNotImplemented)
}
}
public func kr_getPaths(args: Any?) -> [String:String] {
return [
"base": FilePath.sharedDirectory.path,
"working": FilePath.workingDirectory.path,
"temp": FilePath.cacheDirectory.path
]
}
}
+50
View File
@@ -0,0 +1,50 @@
import Foundation
import Combine
import Libcore
public class KRGroupsEventHandler: NSObject, FlutterPlugin, FlutterStreamHandler{
static let name = "\(Bundle.main.serviceIdentifier)/groups"
private var commandClient: CommandClient?
private var channel: FlutterEventChannel?
private var events: FlutterEventSink?
private var cancellable: AnyCancellable?
public static func register(with registrar: FlutterPluginRegistrar) {
let instance = KRGroupsEventHandler()
instance.channel = FlutterEventChannel(name: Self.name,
binaryMessenger: registrar.messenger())
instance.channel?.setStreamHandler(instance)
}
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
FileManager.default.changeCurrentDirectoryPath(FilePath.sharedDirectory.path)
self.events = events
commandClient = CommandClient(.groups)
commandClient?.connect()
cancellable = commandClient?.$groups.sink{ [self] groups in
self.kr_writeGroups(groups)
}
return nil
}
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
commandClient?.disconnect()
cancellable?.cancel()
events = nil
return nil
}
func kr_writeGroups(_ sbGroups: [SBGroup]?) {
guard let sbGroups else {return}
if
let groups = try? JSONEncoder().encode(sbGroups),
let groups = String(data: groups, encoding: .utf8)
{
DispatchQueue.main.async { [events = self.events, groups] in
events?(groups)
}
}
}
}
+49
View File
@@ -0,0 +1,49 @@
import Foundation
import Combine
import Libcore
class KRLogsEventHandler: NSObject, FlutterPlugin, FlutterStreamHandler {
static let name = "\(Bundle.main.serviceIdentifier)/service.logs"
private var commandClient: CommandClient?
private var events: FlutterEventSink?
private var channel: FlutterEventChannel?
private var cancellable: AnyCancellable?
public static func register(with registrar: FlutterPluginRegistrar) {
let instance = KRLogsEventHandler()
instance.channel = FlutterEventChannel(name: Self.name,
binaryMessenger: registrar.messenger())
instance.channel?.setStreamHandler(instance)
}
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
FileManager.default.changeCurrentDirectoryPath(FilePath.sharedDirectory.path)
self.events = events
commandClient = CommandClient(.log)
commandClient?.connect()
cancellable = commandClient?.$logList.sink{ [self] logs in
events(logs)
}
return nil
}
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
commandClient?.disconnect()
cancellable?.cancel()
events = nil
return nil
}
}
/*
extension KRLogsEventHandler {
public func kr_clearLog() {}
public func kr_connected() {}
public func kr_disconnected(_ message: String?) {}
public func kr_initializeClashMode(_ modeList: LibboxStringIteratorProtocol?, currentMode: String?) {}
public func kr_updateClashMode(_ newMode: String?) {}
public func kr_writeGroups(_ message: LibboxOutboundGroupIteratorProtocol?) {}
public func kr_writeStatus(_ message: LibboxStatusMessage?) {}
}
*/
+232
View File
@@ -0,0 +1,232 @@
//
// MethodHandler.swift
// Runner
//
// Created by GFWFighter on 10/23/23.
//
import Flutter
import Combine
import Libcore
public class KRMethodHandler: NSObject, FlutterPlugin {
private var cancelBag: Set<AnyCancellable> = []
//
private var cancellables = Set<AnyCancellable>()
public static let name = "\(Bundle.main.serviceIdentifier)/method"
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: Self.name, binaryMessenger: registrar.messenger())
let instance = KRMethodHandler()
registrar.addMethodCallDelegate(instance, channel: channel)
instance.channel = channel
}
private var channel: FlutterMethodChannel?
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
@Sendable func kr_mainResult(_ res: Any?) async -> Void {
await MainActor.run {
result(res)
}
}
switch call.method {
case "parse_config":
guard
let args = call.arguments as? [String:Any?],
let path = args["path"] as? String,
let tempPath = args["tempPath"] as? String,
let debug = (args["debug"] as? NSNumber)?.boolValue
else {
result(FlutterError(code: "INVALID_ARGS", message: nil, details: nil))
return
}
var error: NSError?
MobileParse(path, tempPath, debug, &error)
if let error {
result(FlutterError(code: String(error.code), message: error.description, details: nil))
return
}
result("")
case "change_hiddify_options":
guard let options = call.arguments as? String else {
result(FlutterError(code: "INVALID_ARGS", message: nil, details: nil))
return
}
VPNConfig.shared.configOptions = options
result(true)
case "setup":
Task {
do {
try await VPNManager.shared.setup()
} catch {
await kr_mainResult(FlutterError(code: "SETUP", message: error.localizedDescription, details: nil))
return
}
await kr_mainResult(true)
}
case "start":
Task {
guard
let args = call.arguments as? [String:Any?],
let path = args["path"] as? String
else {
await kr_mainResult(FlutterError(code: "INVALID_ARGS", message: nil, details: nil))
return
}
VPNConfig.shared.activeConfigPath = path
var error: NSError?
let config = MobileBuildConfig(path, VPNConfig.shared.configOptions, &error)
print(config);
if let error {
await kr_mainResult(FlutterError(code: String(error.code), message: error.description, details: nil))
return
}
do {
try await VPNManager.shared.setup()
try await VPNManager.shared.connect(with: config, disableMemoryLimit: VPNConfig.shared.disableMemoryLimit)
} catch {
await kr_mainResult(FlutterError(code: "SETUP_CONNECTION", message: error.localizedDescription, details: nil))
return
}
await kr_mainResult(true)
}
case "restart":
Task { [unowned self] in
guard
let args = call.arguments as? [String:Any?],
let path = args["path"] as? String
else {
await kr_mainResult(FlutterError(code: "INVALID_ARGS", message: nil, details: nil))
return
}
VPNConfig.shared.activeConfigPath = path
VPNManager.shared.disconnect()
do {
try await kr_waitForStop().value
} catch {
await kr_mainResult(FlutterError(code: "SETUP_CONNECTION", message: error.localizedDescription, details: nil))
return
}
var error: NSError?
let config = MobileBuildConfig(path, VPNConfig.shared.configOptions, &error)
if let error {
await kr_mainResult(FlutterError(code: "BUILD_CONFIG", message: error.localizedDescription, details: nil))
return
}
do {
try await VPNManager.shared.setup()
try await VPNManager.shared.connect(with: config, disableMemoryLimit: VPNConfig.shared.disableMemoryLimit)
} catch {
await kr_mainResult(FlutterError(code: "SETUP_CONNECTION", message: error.localizedDescription, details: nil))
return
}
await kr_mainResult(true)
}
case "stop":
VPNManager.shared.disconnect()
result(true)
case "reset":
VPNManager.shared.reset()
result(true)
case "url_test":
guard
let args = call.arguments as? [String:Any?]
else {
result(FlutterError(code: "INVALID_ARGS", message: nil, details: nil))
return
}
let group = args["groupTag"] as? String
FileManager.default.changeCurrentDirectoryPath(FilePath.sharedDirectory.path)
do {
try LibboxNewStandaloneCommandClient()?.urlTest(group)
} catch {
result(FlutterError(code: "URL_TEST", message: error.localizedDescription, details: nil))
return
}
result(true)
case "select_outbound":
guard
let args = call.arguments as? [String:Any?],
let group = args["groupTag"] as? String,
let outbound = args["outboundTag"] as? String
else {
result(FlutterError(code: "INVALID_ARGS", message: nil, details: nil))
return
}
FileManager.default.changeCurrentDirectoryPath(FilePath.sharedDirectory.path)
do {
try LibboxNewStandaloneCommandClient()?.selectOutbound(group, outboundTag: outbound)
} catch {
result(FlutterError(code: "SELECT_OUTBOUND", message: error.localizedDescription, details: nil))
return
}
result(true)
case "generate_config":
guard
let args = call.arguments as? [String:Any?],
let path = args["path"] as? String
else {
result(FlutterError(code: "INVALID_ARGS", message: nil, details: nil))
return
}
var error: NSError?
let config = MobileBuildConfig(path, VPNConfig.shared.configOptions, &error)
if let error {
result(FlutterError(code: "BUILD_CONFIG", message: error.localizedDescription, details: nil))
return
}
result(config)
case "generate_warp_config":
guard let args = call.arguments as? [String: Any],
let licenseKey = args["license-key"] as? String,
let accountId = args["previous-account-id"] as? String,
let accessToken = args["previous-access-token"] as? String else {
result(FlutterError(code: "INVALID_ARGS", message: nil, details: nil))
return
}
let warpConfig = MobileGenerateWarpConfig(licenseKey, accountId, accessToken, nil)
result(warpConfig)
default:
result(FlutterMethodNotImplemented)
}
}
private func kr_waitForStop(timeout: TimeInterval = 1) -> Future<Void, Error> {
Future { promise in
print("开始等待 VPN 停止...")
let timeoutTimer = Timer.scheduledTimer(withTimeInterval: timeout, repeats: false) { _ in
promise(.failure(NSError(domain: "VPNError", code: -1, userInfo: [NSLocalizedDescriptionKey: "等待 VPN 停止超时"])))
}
VPNManager.shared.$state
.handleEvents(receiveSubscription: { _ in
print("订阅状态变化")
}, receiveOutput: { state in
print("当前 VPN 状态: \(state)")
})
.filter { $0 == .disconnected }
.first()
.delay(for: 0.5, scheduler: DispatchQueue.main)
.sink { _ in
timeoutTimer.invalidate()
print("VPN 已停止")
promise(.success(()))
}
.store(in: &self.cancellables)
}
}
}
+41
View File
@@ -0,0 +1,41 @@
//
// PlatformMethodHandler.swift
// Runner
//
// Created by Hiddify on 12/27/23.
//
import Flutter
import Combine
import Libcore
public class KRPlatformMethodHandler: NSObject, FlutterPlugin {
public static let name = "\(Bundle.main.serviceIdentifier)/platform"
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: Self.name, binaryMessenger: registrar.messenger())
let instance = KRPlatformMethodHandler()
registrar.addMethodCallDelegate(instance, channel: channel)
instance.channel = channel
}
private var channel: FlutterMethodChannel?
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "get_paths":
result(kr_getPaths(args: call.arguments))
default:
result(FlutterMethodNotImplemented)
}
}
public func kr_getPaths(args: Any?) -> [String:String] {
return [
"base": FilePath.sharedDirectory.path,
"working": FilePath.workingDirectory.path,
"temp": FilePath.cacheDirectory.path
]
}
}
+54
View File
@@ -0,0 +1,54 @@
import Foundation
import Flutter
import Combine
import Libcore
public class KRStatsEventHandler: NSObject, FlutterPlugin, FlutterStreamHandler {
static let name = "\(Bundle.main.serviceIdentifier)/stats"
private var commandClient: CommandClient?
private var channel: FlutterEventChannel?
private var events: FlutterEventSink?
private var cancellable: AnyCancellable?
public static func register(with registrar: FlutterPluginRegistrar) {
let instance = KRStatsEventHandler()
instance.channel = FlutterEventChannel(name: Self.name,
binaryMessenger: registrar.messenger(),
codec: FlutterJSONMethodCodec())
instance.channel?.setStreamHandler(instance)
}
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
FileManager.default.changeCurrentDirectoryPath(FilePath.sharedDirectory.path)
self.events = events
commandClient = CommandClient(.status)
commandClient?.connect()
cancellable = commandClient?.$status.sink{ [self] status in
self.kr_writeStatus(status)
}
return nil
}
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
commandClient?.disconnect()
cancellable?.cancel()
events = nil
return nil
}
func kr_writeStatus(_ message: LibboxStatusMessage?) {
guard let message else { return }
let data = [
"connections-in": message.connectionsIn,
"connections-out": message.connectionsOut,
"uplink": message.uplink,
"downlink": message.downlink,
"uplink-total": message.uplinkTotal,
"downlink-total": message.downlinkTotal
] as [String:Any]
events?(data)
}
}
+46
View File
@@ -0,0 +1,46 @@
//
// StatusEventHandler.swift
// Runner
//
// Created by GFWFighter on 10/24/23.
//
import Foundation
import Combine
public class KRStatusEventHandler: NSObject, FlutterPlugin, FlutterStreamHandler {
static let name = "\(Bundle.main.serviceIdentifier)/service.status"
private var channel: FlutterEventChannel?
private var cancellable: AnyCancellable?
public static func register(with registrar: FlutterPluginRegistrar) {
let instance = KRStatusEventHandler()
instance.channel = FlutterEventChannel(name: Self.name, binaryMessenger: registrar.messenger(), codec: FlutterJSONMethodCodec())
instance.channel?.setStreamHandler(instance)
}
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
cancellable = VPNManager.shared.$state.sink { [events] status in
switch status {
case .reasserting, .connecting:
events(["status": "Starting"])
case .connected:
events(["status": "Started"])
case .disconnecting:
events(["status": "Stopping"])
case .disconnected, .invalid:
events(["status": "Stopped"])
@unknown default:
events(["status": "Stopped"])
}
}
return nil
}
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
cancellable?.cancel()
return nil
}
}
+87
View File
@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BASE_BUNDLE_IDENTIFIER</key>
<string>$(BASE_BUNDLE_IDENTIFIER)</string>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>BearVPN</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>baer_with_panels</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.BearVPN.ios</string>
<key>CFBundleURLSchemes</key>
<array>
<string>hiddify</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>EXAppExtensionAttributes</key>
<dict>
<key>EXExtensionPointIdentifier</key>
<string>com.apple.appintents-extension</string>
</dict>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>需要相机权限以支持拍照功能</string>
<key>NSMicrophoneUsageDescription</key>
<string>需要麦克风权限以支持语音消息功能</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>需要相册权限以支持图片上传功能</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>需要相册添加权限以保存图片</string>
<key>SERVICE_IDENTIFIER</key>
<string>$(SERVICE_IDENTIFIER)</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiresFullScreen</key>
<true/>
<key>UIStatusBarHidden</key>
<false/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
+41
View File
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>C617.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>E174.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>1C8F.1</string>
</array>
</dict>
</array>
</dict>
</plist>
+1
View File
@@ -0,0 +1 @@
#import "GeneratedPluginRegistrant.h"
+22
View File
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>packet-tunnel-provider</string>
</array>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.$(BASE_BUNDLE_IDENTIFIER)</string>
</array>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>
+22
View File
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>packet-tunnel-provider</string>
</array>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.app.baer.com</string>
</array>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>
+86
View File
@@ -0,0 +1,86 @@
//
// Stored.swift
// Runner
//
// Created by GFWFighter on 10/24/23.
//
import Foundation
import Combine
enum StoredLocation {
case standard
func data(for key: String) -> Data? {
switch self {
case .standard:
return UserDefaults.standard.data(forKey: key)
}
}
func set(_ value: Data, for key: String) {
switch self {
case .standard:
UserDefaults.standard.set(value, forKey: key)
}
}
}
@propertyWrapper
struct Stored<Value: Codable> {
let location: StoredLocation
let key: String
var wrappedValue: Value {
willSet { // Before modifying wrappedValue
publisher.subject.send(newValue)
guard let value = try? JSONEncoder().encode(newValue) else {
return
}
location.set(value, for: key)
}
}
var projectedValue: Publisher {
publisher
}
private var publisher: Publisher
struct Publisher: Combine.Publisher {
typealias Output = Value
typealias Failure = Never
var subject: CurrentValueSubject<Value, Never> // PassthroughSubject will lack the call of initial assignment
func receive<S>(subscriber: S) where S: Subscriber, Self.Failure == S.Failure, Self.Output == S.Input {
subject.subscribe(subscriber)
}
init(_ output: Output) {
subject = .init(output)
}
}
init(wrappedValue: Value, key: String, in location: StoredLocation = .standard) {
self.location = location
self.key = key
var value = wrappedValue
if let data = location.data(for: key) {
do {
value = try JSONDecoder().decode(Value.self, from: data)
} catch {}
}
self.wrappedValue = value
publisher = Publisher(value)
}
static subscript<OuterSelf: ObservableObject>(
_enclosingInstance observed: OuterSelf,
wrapped wrappedKeyPath: ReferenceWritableKeyPath<OuterSelf, Value>,
storage storageKeyPath: ReferenceWritableKeyPath<OuterSelf, Self>
) -> Value {
get {
observed[keyPath: storageKeyPath].wrappedValue
}
set {
if let subject = observed.objectWillChange as? ObservableObjectPublisher {
subject.send() // Before modifying wrappedValue
observed[keyPath: storageKeyPath].wrappedValue = newValue
}
}
}
}
+22
View File
@@ -0,0 +1,22 @@
//
// VPNConfig.swift
// Runner
//
// Created by GFWFighter on 10/24/23.
//
import Foundation
import Combine
class VPNConfig: ObservableObject {
static let shared = VPNConfig()
@Stored(key: "VPN.ActiveConfigPath")
var activeConfigPath: String = ""
@Stored(key: "VPN.ConfigOptions")
var configOptions: String = ""
@Stored(key: "VPN.DisableMemoryLimit")
var disableMemoryLimit: Bool = false
}
+214
View File
@@ -0,0 +1,214 @@
//
// VPNManager.swift
// Runner
//
// Created by GFWFighter on 7/25/1402 AP.
//
import Foundation
import Combine
import NetworkExtension
enum VPNManagerAlertType: String {
case RequestVPNPermission
case RequestNotificationPermission
case EmptyConfiguration
case StartCommandServer
case CreateService
case StartService
}
struct VPNManagerAlert {
let alert: VPNManagerAlertType?
let message: String?
}
class VPNManager: ObservableObject {
private var cancelBag: Set<AnyCancellable> = []
private var observer: NSObjectProtocol?
private var manager = NEVPNManager.shared()
private var loaded: Bool = false
private var timer: Timer?
static let shared: VPNManager = VPNManager()
@Published private(set) var state: NEVPNStatus = .invalid
@Published private(set) var alert: VPNManagerAlert = .init(alert: nil, message: nil)
@Published private(set) var upload: Int64 = 0
@Published private(set) var download: Int64 = 0
@Published private(set) var elapsedTime: TimeInterval = 0
private var _connectTime: Date?
private var connectTime: Date? {
set {
UserDefaults(suiteName: FilePath.groupName)?.set(newValue?.timeIntervalSince1970, forKey: "SingBoxConnectTime")
_connectTime = newValue
}
get {
if let _connectTime {
return _connectTime
}
guard let interval = UserDefaults(suiteName: FilePath.groupName)?.value(forKey: "SingBoxConnectTime") as? TimeInterval else {
return nil
}
return Date(timeIntervalSince1970: interval)
}
}
private var readingWS: Bool = false
@Published var isConnectedToAnyVPN: Bool = false
init() {
observer = NotificationCenter.default.addObserver(forName: .NEVPNStatusDidChange, object: nil, queue: nil) { [weak self] notification in
guard let connection = notification.object as? NEVPNConnection else { return }
self?.state = connection.status
}
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
guard let self else { return }
updateStats()
elapsedTime = -1 * (connectTime?.timeIntervalSinceNow ?? 0)
}
}
deinit {
if let observer {
NotificationCenter.default.removeObserver(observer)
}
timer?.invalidate()
}
func setup() async throws {
// guard !loaded else { return }
loaded = true
do {
try await loadVPNPreference()
} catch {
print(error.localizedDescription)
}
}
private func loadVPNPreference() async throws {
do {
let managers = try await NETunnelProviderManager.loadAllFromPreferences()
if let manager = managers.first {
self.manager = manager
return
}
let newManager = NETunnelProviderManager()
let `protocol` = NETunnelProviderProtocol()
`protocol`.providerBundleIdentifier = Bundle.main.baseBundleIdentifier + ".PacketTunnel"
`protocol`.serverAddress = "localhost"
newManager.protocolConfiguration = `protocol`
newManager.localizedDescription = "BearVPN"
try await newManager.saveToPreferences()
try await newManager.loadFromPreferences()
self.manager = newManager
} catch {
print(error.localizedDescription)
}
}
private func enableVPNManager() async throws {
manager.isEnabled = true
do {
try await manager.saveToPreferences()
try await manager.loadFromPreferences()
} catch {
print(error.localizedDescription)
}
}
@MainActor private func set(upload: Int64, download: Int64) {
self.upload = upload
self.download = download
}
var isAnyVPNConnected: Bool {
guard let cfDict = CFNetworkCopySystemProxySettings() else { return false }
let nsDict = cfDict.takeRetainedValue() as NSDictionary
guard let keys = nsDict["__SCOPED__"] as? NSDictionary else {
return false
}
for key: String in keys.allKeys as! [String] {
if key == "tap" || key == "tun" || key == "ppp" || key == "ipsec" || key == "ipsec0" {
return true
} else if key.starts(with: "utun") {
return true
}
}
return false
}
func reset() {
loaded = false
if state != .disconnected && state != .invalid {
disconnect()
}
$state.filter { $0 == .disconnected || $0 == .invalid }.first().sink { [weak self] _ in
Task { [weak self] () in
self?.manager = .shared()
do {
let managers = try await NETunnelProviderManager.loadAllFromPreferences()
for manager in managers ?? [] {
try await manager.removeFromPreferences()
}
try await self?.loadVPNPreference()
} catch {
print(error.localizedDescription)
}
}
}.store(in: &cancelBag)
}
private func updateStats() {
let isAnyVPNConnected = self.isAnyVPNConnected
if isConnectedToAnyVPN != isAnyVPNConnected {
isConnectedToAnyVPN = isAnyVPNConnected
}
guard state == .connected else { return }
guard let connection = manager.connection as? NETunnelProviderSession else { return }
do {
try connection.sendProviderMessage("stats".data(using: .utf8)!) { [weak self] response in
guard
let response,
let response = String(data: response, encoding: .utf8)
else { return }
let responseComponents = response.components(separatedBy: ",")
guard
responseComponents.count == 2,
let upload = Int64(responseComponents[0]),
let download = Int64(responseComponents[1])
else { return }
Task { [upload, download, weak self] () in
await self?.set(upload: upload, download: download)
}
}
} catch {
print(error.localizedDescription)
}
}
func connect(with config: String, disableMemoryLimit: Bool = false) async throws {
await set(upload: 0, download: 0)
guard state == .disconnected else { return }
do {
try await enableVPNManager()
try manager.connection.startVPNTunnel(options: [
"Config": config as NSString,
"DisableMemoryLimit": (disableMemoryLimit ? "YES" : "NO") as NSString,
])
} catch {
print(error.localizedDescription)
}
connectTime = .now
}
func disconnect() {
guard state == .connected else { return }
manager.connection.stopVPNTunnel()
}
}
+12
View File
@@ -0,0 +1,12 @@
import Flutter
import UIKit
import XCTest
class RunnerTests: XCTestCase {
func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}
+166
View File
@@ -0,0 +1,166 @@
import Foundation
import Libcore
public class CommandClient: ObservableObject {
public enum ConnectionType {
case status
case groups
case log
case groupsInfoOnly
}
private let connectionType: ConnectionType
private let logMaxLines: Int
private var commandClient: LibboxCommandClient?
private var connectTask: Task<Void, Error>?
@Published private(set) var isConnected: Bool
@Published private(set) var status: LibboxStatusMessage?
@Published private(set) var groups: [SBGroup]?
@Published private(set) var logList: [String]
public init(_ connectionType: ConnectionType, logMaxLines: Int = 300) {
self.connectionType = connectionType
self.logMaxLines = logMaxLines
logList = []
isConnected = false
}
public func connect() {
if isConnected {
return
}
if let connectTask {
connectTask.cancel()
}
connectTask = Task {
await connect0()
}
}
public func disconnect() {
if let connectTask {
connectTask.cancel()
self.connectTask = nil
}
if let commandClient {
try? commandClient.disconnect()
self.commandClient = nil
}
}
private nonisolated func connect0() async {
let clientOptions = LibboxCommandClientOptions()
switch connectionType {
case .status:
clientOptions.command = LibboxCommandStatus
case .groups:
clientOptions.command = LibboxCommandGroup
case .log:
clientOptions.command = LibboxCommandLog
case .groupsInfoOnly:
clientOptions.command = LibboxCommandGroupInfoOnly
}
clientOptions.statusInterval = Int64(2 * NSEC_PER_SEC)
let client = LibboxNewCommandClient(clientHandler(self), clientOptions)!
do {
for i in 0 ..< 10 {
try await Task.sleep(nanoseconds: UInt64(Double(100 + (i * 50)) * Double(NSEC_PER_MSEC)))
try Task.checkCancellation()
do {
try client.connect()
await MainActor.run {
commandClient = client
}
return
} catch {}
try Task.checkCancellation()
}
} catch {
try? client.disconnect()
}
}
private class clientHandler: NSObject, LibboxCommandClientHandlerProtocol {
private let commandClient: CommandClient
init(_ commandClient: CommandClient) {
self.commandClient = commandClient
}
func connected() {
DispatchQueue.main.async { [self] in
if commandClient.connectionType == .log {
commandClient.logList = []
}
commandClient.isConnected = true
}
}
func disconnected(_: String?) {
DispatchQueue.main.async { [self] in
commandClient.isConnected = false
}
}
func clearLog() {
DispatchQueue.main.async { [self] in
commandClient.logList.removeAll()
}
}
func writeLog(_ message: String?) {
guard let message else {
return
}
DispatchQueue.main.async { [self] in
if commandClient.logList.count > commandClient.logMaxLines {
commandClient.logList.removeFirst()
}
commandClient.logList.append(message)
}
}
func writeStatus(_ message: LibboxStatusMessage?) {
DispatchQueue.main.async { [self] in
commandClient.status = message
}
}
func writeGroups(_ groups: LibboxOutboundGroupIteratorProtocol?) {
guard let groups else {
return
}
var sbGroups = [SBGroup]()
while groups.hasNext() {
let group = groups.next()!
var items = [SBItem]()
let groupItems = group.getItems()
while groupItems?.hasNext() ?? false {
let item = groupItems?.next()!
items.append(SBItem(tag: item!.tag,
type: item!.type,
urlTestDelay: Int(item!.urlTestDelay)
)
)
}
sbGroups.append(.init(tag: group.tag,
type: group.type,
selected: group.selected,
items: items)
)
}
DispatchQueue.main.async { [self] in
commandClient.groups = sbGroups
}
}
func initializeClashMode(_ modeList: LibboxStringIteratorProtocol?, currentMode: String?) {
}
func updateClashMode(_ newMode: String?) {
}
}
}
+38
View File
@@ -0,0 +1,38 @@
//
// FilePath.swift
// SingBoxPacketTunnel
//
// Created by GFWFighter on 7/25/1402 AP.
//
import Foundation
public enum FilePath {
public static let packageName = {
Bundle.main.infoDictionary?["BASE_BUNDLE_IDENTIFIER"] as? String ?? "unknown"
}()
}
public extension FilePath {
static let groupName = "group.\(packageName)"
private static let defaultSharedDirectory: URL! = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: FilePath.groupName)
static let sharedDirectory = defaultSharedDirectory!
static let cacheDirectory = sharedDirectory
.appendingPathComponent("Library", isDirectory: true)
.appendingPathComponent("Caches", isDirectory: true)
static let workingDirectory = cacheDirectory.appendingPathComponent("Working", isDirectory: true)
}
public extension URL {
var fileName: String {
var path = relativePath
if let index = path.lastIndex(of: "/") {
path = String(path[path.index(index, offsetBy: 1)...])
}
return path
}
}
+18
View File
@@ -0,0 +1,18 @@
public struct SBItem: Codable {
let tag: String
let type: String
let urlTestDelay: Int
enum CodingKeys: String, CodingKey {
case tag
case type
case urlTestDelay = "url-test-delay"
}
}
public struct SBGroup: Codable {
let tag: String
let type: String
let selected: String
let items: [SBItem]
}
+29
View File
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>destination</key>
<string>export</string>
<key>manageAppVersionAndBuildNumber</key>
<true/>
<key>method</key>
<string>app-store</string>
<key>provisioningProfiles</key>
<dict>
<key>app.myhiddify.com</key>
<string>dist.apple.app.myhiddify.com</string>
<key>app.myhiddify.com.SingBoxPacketTunnel</key>
<string>dist.apple.app.myhiddify.com.singboxpackettunnel</string>
</dict>
<key>signingCertificate</key>
<string>E2217AF6F3AD11BA09F9FD95E025D7E637F8B081</string>
<key>signingStyle</key>
<string>manual</string>
<key>stripSwiftSymbols</key>
<true/>
<key>teamID</key>
<string>3UR892FAP3</string>
<key>uploadSymbols</key>
<true/>
</dict>
</plist>
+42
View File
@@ -0,0 +1,42 @@
display_name: Hiddify
icon: ..\..\assets\images\windows.ico
keywords:
- Hiddify
- Proxy
- VPN
- V2ray
- Nekoray
- Xray
- Psiphon
- OpenVPN
generic_name: Hiddify
actions:
- name: Start
label: start
arguments:
- --start
- name: Stop
label: stop
arguments:
- --stop
categories:
- Internet
startup_notify: true
# You can specify the shared libraries that you want to bundle with your app
#
# flutter_distributor automatically detects the shared libraries that your app
# depends on, but you can also specify them manually here.
#
# The following example shows how to bundle the libcurl library with your app.
#
# include:
# - libcurl.so.4
include: []