Compare commits

...
Sign in to create a new pull request.

2 commits

Author SHA1 Message Date
Conrad Kramer
d4a2d76537 Updated to Xcode 15.2 2024-01-20 09:52:30 -08:00
Conrad Kramer
436a67b352 Update Tunnel on the main thread
Also updated it to use the new Swift Observable macro
2024-01-20 09:45:47 -08:00
12 changed files with 172 additions and 74 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -2,13 +2,13 @@ import NetworkExtension
extension NEVPNManager {
func remove() async throws {
let _: Void = try await withUnsafeThrowingContinuation { continuation in
_ = try await withUnsafeThrowingContinuation { continuation in
removeFromPreferences(completionHandler: completion(continuation))
}
}
func save() async throws {
let _: Void = try await withUnsafeThrowingContinuation { continuation in
_ = try await withUnsafeThrowingContinuation { continuation in
saveToPreferences(completionHandler: completion(continuation))
}
}
@ -18,13 +18,7 @@ extension NETunnelProviderManager {
class var managers: [NETunnelProviderManager] {
get async throws {
try await withUnsafeThrowingContinuation { continuation in
loadAllFromPreferences { managers, error in
if let error = error {
continuation.resume(throwing: error)
} else {
continuation.resume(returning: managers ?? [])
}
}
loadAllFromPreferences(completionHandler: completion(continuation))
}
}
}
@ -32,10 +26,20 @@ extension NETunnelProviderManager {
private func completion(_ continuation: UnsafeContinuation<Void, Error>) -> (Error?) -> Void {
return { error in
if let error = error {
if let error {
continuation.resume(throwing: error)
} else {
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,15 +2,16 @@ import Combine
import NetworkExtension
import SwiftUI
@MainActor
class Tunnel: ObservableObject {
@Published private(set) var status: Status = .unknown
@Published private var error: NEVPNError?
@Observable class Tunnel {
private(set) var status: Status = .unknown
private var error: NEVPNError?
private let bundleIdentifier: String
private let configure: (NETunnelProviderManager, NETunnelProviderProtocol) -> Void
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]? {
didSet { status = currentStatus }
}
@ -48,24 +49,31 @@ class Tunnel: ObservableObject {
self.bundleIdentifier = bundleIdentifier
self.configure = configure
listenForUpdates()
Task { await update() }
}
private func listenForUpdates() {
let center = NotificationCenter.default
let statusTask = Task {
for try await _ in NotificationCenter.default.notifications(named: .NEVPNStatusDidChange) {
for try await _ in center.notifications(named: .NEVPNStatusDidChange).map({ _ in () }) {
status = currentStatus
}
}
let configurationTask = Task {
for try await _ in NotificationCenter.default.notifications(named: .NEVPNConfigurationChange) {
for try await _ in center.notifications(named: .NEVPNConfigurationChange).map({ _ in () }) {
await update()
}
}
tasks = [statusTask, configurationTask]
}
func update() async {
private func update() async {
do {
managers = try await NETunnelProviderManager.managers
} catch let error as NEVPNError {
self.error = error
let updated = try await NETunnelProviderManager.managers
await MainActor.run { managers = updated }
} catch let vpnError as NEVPNError {
error = vpnError
} catch {
print(error)
}
@ -109,7 +117,9 @@ class Tunnel: ObservableObject {
}
deinit {
tasks.forEach { $0.cancel() }
for task in tasks {
task.cancel()
}
}
}

View file

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

View file

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

View file

@ -0,0 +1,86 @@
{
"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"?>
<Scheme
LastUpgradeVersion = "1430"
LastUpgradeVersion = "1510"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"

View file

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

View file

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