Compare commits

..

No commits in common. "d4a2d7653714a26294394e67444a5810fb8992fc" and "b008762a5be1d4feb24de026bb7487dfe4f56e39" have entirely different histories.

12 changed files with 74 additions and 172 deletions

View file

@ -9,7 +9,7 @@ on:
jobs: jobs:
build: build:
name: Build App (${{ matrix.platform }}) name: Build App (${{ matrix.platform }})
runs-on: macos-14 runs-on: macos-12
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@ -21,7 +21,7 @@ jobs:
rust-targets: rust-targets:
- aarch64-apple-ios - aarch64-apple-ios
- scheme: App - scheme: App
destination: platform=iOS Simulator,OS=17.2,name=iPhone 15 Pro destination: platform=iOS Simulator,OS=16.2,name=iPhone 14 Pro
platform: iOS Simulator platform: iOS Simulator
sdk-name: iphonesimulator sdk-name: iphonesimulator
rust-targets: rust-targets:
@ -35,7 +35,7 @@ jobs:
- x86_64-apple-darwin - x86_64-apple-darwin
- aarch64-apple-darwin - aarch64-apple-darwin
env: env:
DEVELOPER_DIR: /Applications/Xcode_15.2.app/Contents/Developer DEVELOPER_DIR: /Applications/Xcode_14.2.app/Contents/Developer
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3

View file

@ -6,7 +6,7 @@ on:
jobs: jobs:
build: build:
name: Build ${{ matrix.configuration['platform'] }} Release name: Build ${{ matrix.configuration['platform'] }} Release
runs-on: macos-14 runs-on: macos-12
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@ -22,7 +22,7 @@ jobs:
method: mac-application method: mac-application
artifact-file: Burrow.app.txz artifact-file: Burrow.app.txz
env: env:
DEVELOPER_DIR: /Applications/Xcode_15.2.app/Contents/Developer DEVELOPER_DIR: /Applications/Xcode_14.2.app/Contents/Developer
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3

View file

@ -15,7 +15,7 @@ struct BurrowApp: App {
var body: some Scene { var body: some Scene {
WindowGroup { WindowGroup {
TunnelView(tunnel: Self.tunnel) TunnelView()
} }
} }
} }

View file

@ -8,7 +8,7 @@
import SwiftUI import SwiftUI
struct MenuItemToggleView: View { struct MenuItemToggleView: View {
var tunnel: Tunnel @ObservedObject var tunnel: Tunnel
var body: some View { var body: some View {
HStack { HStack {
@ -23,6 +23,7 @@ struct MenuItemToggleView: View {
.padding(.horizontal, 4) .padding(.horizontal, 4)
.padding(10) .padding(10)
.frame(minWidth: 300, minHeight: 32, maxHeight: 32) .frame(minWidth: 300, minHeight: 32, maxHeight: 32)
.task { await tunnel.update() }
} }
} }

View file

@ -2,13 +2,13 @@ import NetworkExtension
extension NEVPNManager { extension NEVPNManager {
func remove() async throws { func remove() async throws {
_ = try await withUnsafeThrowingContinuation { continuation in let _: Void = try await withUnsafeThrowingContinuation { continuation in
removeFromPreferences(completionHandler: completion(continuation)) removeFromPreferences(completionHandler: completion(continuation))
} }
} }
func save() async throws { func save() async throws {
_ = try await withUnsafeThrowingContinuation { continuation in let _: Void = try await withUnsafeThrowingContinuation { continuation in
saveToPreferences(completionHandler: completion(continuation)) saveToPreferences(completionHandler: completion(continuation))
} }
} }
@ -18,7 +18,13 @@ extension NETunnelProviderManager {
class var managers: [NETunnelProviderManager] { class var managers: [NETunnelProviderManager] {
get async throws { get async throws {
try await withUnsafeThrowingContinuation { continuation in try await withUnsafeThrowingContinuation { continuation in
loadAllFromPreferences(completionHandler: completion(continuation)) loadAllFromPreferences { managers, error in
if let error = error {
continuation.resume(throwing: error)
} else {
continuation.resume(returning: managers ?? [])
}
}
} }
} }
} }
@ -26,20 +32,10 @@ extension NETunnelProviderManager {
private func completion(_ continuation: UnsafeContinuation<Void, Error>) -> (Error?) -> Void { private func completion(_ continuation: UnsafeContinuation<Void, Error>) -> (Error?) -> Void {
return { error in return { error in
if let error { if let error = error {
continuation.resume(throwing: error) continuation.resume(throwing: error)
} else { } else {
continuation.resume(returning: ()) continuation.resume(returning: ())
} }
} }
} }
private func completion<T>(_ continuation: UnsafeContinuation<T, Error>) -> (T?, Error?) -> Void {
return { value, error in
if let error {
continuation.resume(throwing: error)
} else if let value {
continuation.resume(returning: value)
}
}
}

View file

@ -2,16 +2,15 @@ import Combine
import NetworkExtension import NetworkExtension
import SwiftUI import SwiftUI
@Observable class Tunnel { @MainActor
private(set) var status: Status = .unknown class Tunnel: ObservableObject {
private var error: NEVPNError? @Published private(set) var status: Status = .unknown
@Published private var error: NEVPNError?
private let bundleIdentifier: String private let bundleIdentifier: String
private let configure: (NETunnelProviderManager, NETunnelProviderProtocol) -> Void private let configure: (NETunnelProviderManager, NETunnelProviderProtocol) -> Void
private var tasks: [Task<Void, Error>] = [] private var tasks: [Task<Void, Error>] = []
// Each manager corresponds to one entry in the Settings app.
// Our goal is to maintain a single manager, so we create one if none exist and delete extra if there are any.
private var managers: [NEVPNManager]? { private var managers: [NEVPNManager]? {
didSet { status = currentStatus } didSet { status = currentStatus }
} }
@ -49,31 +48,24 @@ import SwiftUI
self.bundleIdentifier = bundleIdentifier self.bundleIdentifier = bundleIdentifier
self.configure = configure self.configure = configure
listenForUpdates()
Task { await update() }
}
private func listenForUpdates() {
let center = NotificationCenter.default
let statusTask = Task { let statusTask = Task {
for try await _ in center.notifications(named: .NEVPNStatusDidChange).map({ _ in () }) { for try await _ in NotificationCenter.default.notifications(named: .NEVPNStatusDidChange) {
status = currentStatus status = currentStatus
} }
} }
let configurationTask = Task { let configurationTask = Task {
for try await _ in center.notifications(named: .NEVPNConfigurationChange).map({ _ in () }) { for try await _ in NotificationCenter.default.notifications(named: .NEVPNConfigurationChange) {
await update() await update()
} }
} }
tasks = [statusTask, configurationTask] tasks = [statusTask, configurationTask]
} }
private func update() async { func update() async {
do { do {
let updated = try await NETunnelProviderManager.managers managers = try await NETunnelProviderManager.managers
await MainActor.run { managers = updated } } catch let error as NEVPNError {
} catch let vpnError as NEVPNError { self.error = error
error = vpnError
} catch { } catch {
print(error) print(error)
} }
@ -117,9 +109,7 @@ import SwiftUI
} }
deinit { deinit {
for task in tasks { tasks.forEach { $0.cancel() }
task.cancel()
}
} }
} }

View file

@ -1,34 +1,36 @@
import SwiftUI import SwiftUI
struct TunnelView: View { struct TunnelView: View {
var tunnel: Tunnel // @ObservedObject var tunnel: Tunnel
var body: some View { var body: some View {
VStack { EmptyView()
Text(verbatim: tunnel.status.description) // VStack {
switch tunnel.status { // Text(verbatim: tunnel.status.description)
case .connected: // switch tunnel.status {
Button("Disconnect", action: stop) // case .connected:
case .permissionRequired: // Button("Disconnect", action: stop)
Button("Allow", action: configure) // case .permissionRequired:
case .disconnected: // Button("Allow", action: configure)
Button("Start", action: start) // case .disconnected:
default: // Button("Start", action: start)
EmptyView() // default:
} // EmptyView()
} // }
.padding() // }
// .task { await tunnel.update() }
// .padding()
} }
private func start() { // private func start() {
try? tunnel.start() // try? tunnel.start()
} // }
//
private func stop() { // private func stop() {
tunnel.stop() // tunnel.stop()
} // }
//
private func configure() { // private func configure() {
Task { try await tunnel.configure() } // Task { try await tunnel.configure() }
} // }
} }

View file

@ -196,7 +196,7 @@
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
D08252712B5C3E2E005DA378 /* PBXTargetDependency */, D0BCC6122A0B328800AD070D /* PBXTargetDependency */,
); );
name = NetworkExtension; name = NetworkExtension;
productName = BurrowNetworkExtension; productName = BurrowNetworkExtension;
@ -215,7 +215,7 @@
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
D08252732B5C3E33005DA378 /* PBXTargetDependency */, D0BCC6142A0B329200AD070D /* PBXTargetDependency */,
D020F65C29E4A697002790F6 /* PBXTargetDependency */, D020F65C29E4A697002790F6 /* PBXTargetDependency */,
); );
name = App; name = App;
@ -231,7 +231,7 @@
attributes = { attributes = {
BuildIndependentTargetsInParallel = 1; BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1430; LastSwiftUpdateCheck = 1430;
LastUpgradeCheck = 1510; LastUpgradeCheck = 1430;
TargetAttributes = { TargetAttributes = {
D020F65229E4A697002790F6 = { D020F65229E4A697002790F6 = {
CreatedOnToolsVersion = 14.3; CreatedOnToolsVersion = 14.3;
@ -251,7 +251,6 @@
); );
mainGroup = D05B9F6929E39EEC008CB1F9; mainGroup = D05B9F6929E39EEC008CB1F9;
packageReferences = ( packageReferences = (
D082526F2B5C3E23005DA378 /* XCRemoteSwiftPackageReference "SwiftLint" */,
); );
productRefGroup = D05B9F7329E39EEC008CB1F9 /* Products */; productRefGroup = D05B9F7329E39EEC008CB1F9 /* Products */;
projectDirPath = ""; projectDirPath = "";
@ -338,13 +337,13 @@
target = D020F65229E4A697002790F6 /* NetworkExtension */; target = D020F65229E4A697002790F6 /* NetworkExtension */;
targetProxy = D020F65B29E4A697002790F6 /* PBXContainerItemProxy */; targetProxy = D020F65B29E4A697002790F6 /* PBXContainerItemProxy */;
}; };
D08252712B5C3E2E005DA378 /* PBXTargetDependency */ = { D0BCC6122A0B328800AD070D /* PBXTargetDependency */ = {
isa = PBXTargetDependency; isa = PBXTargetDependency;
productRef = D08252702B5C3E2E005DA378 /* SwiftLintPlugin */; productRef = D0BCC6112A0B328800AD070D /* SwiftLintPlugin */;
}; };
D08252732B5C3E33005DA378 /* PBXTargetDependency */ = { D0BCC6142A0B329200AD070D /* PBXTargetDependency */ = {
isa = PBXTargetDependency; isa = PBXTargetDependency;
productRef = D08252722B5C3E33005DA378 /* SwiftLintPlugin */; productRef = D0BCC6132A0B329200AD070D /* SwiftLintPlugin */;
}; };
/* End PBXTargetDependency section */ /* End PBXTargetDependency section */
@ -424,25 +423,25 @@
/* End XCConfigurationList section */ /* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */ /* Begin XCRemoteSwiftPackageReference section */
D082526F2B5C3E23005DA378 /* XCRemoteSwiftPackageReference "SwiftLint" */ = { D0BCC6102A0B327700AD070D /* XCRemoteSwiftPackageReference "SwiftLint" */ = {
isa = XCRemoteSwiftPackageReference; isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/realm/SwiftLint.git"; repositoryURL = "https://github.com/realm/SwiftLint.git";
requirement = { requirement = {
kind = upToNextMajorVersion; kind = upToNextMajorVersion;
minimumVersion = 0.54.0; minimumVersion = 0.51.0;
}; };
}; };
/* End XCRemoteSwiftPackageReference section */ /* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */ /* Begin XCSwiftPackageProductDependency section */
D08252702B5C3E2E005DA378 /* SwiftLintPlugin */ = { D0BCC6112A0B328800AD070D /* SwiftLintPlugin */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
package = D082526F2B5C3E23005DA378 /* XCRemoteSwiftPackageReference "SwiftLint" */; package = D0BCC6102A0B327700AD070D /* XCRemoteSwiftPackageReference "SwiftLint" */;
productName = "plugin:SwiftLintPlugin"; productName = "plugin:SwiftLintPlugin";
}; };
D08252722B5C3E33005DA378 /* SwiftLintPlugin */ = { D0BCC6132A0B329200AD070D /* SwiftLintPlugin */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
package = D082526F2B5C3E23005DA378 /* XCRemoteSwiftPackageReference "SwiftLint" */; package = D0BCC6102A0B327700AD070D /* XCRemoteSwiftPackageReference "SwiftLint" */;
productName = "plugin:SwiftLintPlugin"; productName = "plugin:SwiftLintPlugin";
}; };
/* End XCSwiftPackageProductDependency section */ /* End XCSwiftPackageProductDependency section */

View file

@ -1,86 +0,0 @@
{
"pins" : [
{
"identity" : "collectionconcurrencykit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/JohnSundell/CollectionConcurrencyKit.git",
"state" : {
"revision" : "b4f23e24b5a1bff301efc5e70871083ca029ff95",
"version" : "0.2.0"
}
},
{
"identity" : "cryptoswift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/krzyzanowskim/CryptoSwift.git",
"state" : {
"revision" : "7892a123f7e8d0fe62f9f03728b17bbd4f94df5c",
"version" : "1.8.1"
}
},
{
"identity" : "sourcekitten",
"kind" : "remoteSourceControl",
"location" : "https://github.com/jpsim/SourceKitten.git",
"state" : {
"revision" : "b6dc09ee51dfb0c66e042d2328c017483a1a5d56",
"version" : "0.34.1"
}
},
{
"identity" : "swift-argument-parser",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser.git",
"state" : {
"revision" : "fee6933f37fde9a5e12a1e4aeaa93fe60116ff2a",
"version" : "1.2.2"
}
},
{
"identity" : "swift-syntax",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-syntax.git",
"state" : {
"revision" : "6ad4ea24b01559dde0773e3d091f1b9e36175036",
"version" : "509.0.2"
}
},
{
"identity" : "swiftlint",
"kind" : "remoteSourceControl",
"location" : "https://github.com/realm/SwiftLint.git",
"state" : {
"revision" : "f17a4f9dfb6a6afb0408426354e4180daaf49cee",
"version" : "0.54.0"
}
},
{
"identity" : "swiftytexttable",
"kind" : "remoteSourceControl",
"location" : "https://github.com/scottrhoyt/SwiftyTextTable.git",
"state" : {
"revision" : "c6df6cf533d120716bff38f8ff9885e1ce2a4ac3",
"version" : "0.9.0"
}
},
{
"identity" : "swxmlhash",
"kind" : "remoteSourceControl",
"location" : "https://github.com/drmohundro/SWXMLHash.git",
"state" : {
"revision" : "4d0f62f561458cbe1f732171e625f03195151b60",
"version" : "7.0.1"
}
},
{
"identity" : "yams",
"kind" : "remoteSourceControl",
"location" : "https://github.com/jpsim/Yams.git",
"state" : {
"revision" : "0d9ee7ea8c4ebd4a489ad7a73d5c6cad55d6fed3",
"version" : "5.0.6"
}
}
],
"version" : 2
}

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1510" LastUpgradeVersion = "1430"
version = "1.7"> version = "1.7">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1510" LastUpgradeVersion = "1430"
version = "2.0"> version = "2.0">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"

View file

@ -4,8 +4,8 @@ SDKROOT = auto
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES
SUPPORTED_PLATFORMS = iphoneos iphonesimulator macosx SUPPORTED_PLATFORMS = iphoneos iphonesimulator macosx
IPHONEOS_DEPLOYMENT_TARGET = 17.0 IPHONEOS_DEPLOYMENT_TARGET = 15.0
MACOSX_DEPLOYMENT_TARGET = 14.0 MACOSX_DEPLOYMENT_TARGET = 12.0
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO
SUPPORTS_MACCATALYST = NO SUPPORTS_MACCATALYST = NO