diff --git a/.swiftlint.yml b/.swiftlint.yml index 22ef035..d609718 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -46,6 +46,7 @@ opt_in_rules: - multiline_parameters - multiline_parameters_brackets - no_extension_access_modifier +- no_grouping_extension - nslocalizedstring_key - nslocalizedstring_require_bundle - number_separator @@ -75,7 +76,9 @@ opt_in_rules: - sorted_first_last - sorted_imports - static_operator +- strict_fileprivate - strong_iboutlet +- switch_case_on_newline - test_case_accessibility - toggle_bool - trailing_closure @@ -94,5 +97,3 @@ disabled_rules: - force_try - nesting - todo -- trailing_comma -- switch_case_on_newline diff --git a/Apple/App/App.xcconfig b/Apple/App/App.xcconfig index 4e42ddc..1d63205 100644 --- a/Apple/App/App.xcconfig +++ b/Apple/App/App.xcconfig @@ -11,12 +11,7 @@ INFOPLIST_KEY_UIStatusBarStyle[sdk=iphone*] = UIStatusBarStyleDefault INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad[sdk=iphone*] = UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone[sdk=iphone*] = UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight TARGETED_DEVICE_FAMILY[sdk=iphone*] = 1,2 -EXCLUDED_SOURCE_FILE_NAMES = MainMenu.xib -EXCLUDED_SOURCE_FILE_NAMES[sdk=macosx*] = -INFOPLIST_KEY_LSUIElement[sdk=macosx*] = YES -INFOPLIST_KEY_NSMainNibFile[sdk=macosx*] = MainMenu -INFOPLIST_KEY_NSPrincipalClass[sdk=macosx*] = NSApplication INFOPLIST_KEY_LSApplicationCategoryType[sdk=macosx*] = public.app-category.utilities CODE_SIGN_ENTITLEMENTS = App/App-iOS.entitlements diff --git a/Apple/App/AppDelegate.swift b/Apple/App/AppDelegate.swift index 6085d85..f42b52f 100644 --- a/Apple/App/AppDelegate.swift +++ b/Apple/App/AppDelegate.swift @@ -3,7 +3,6 @@ import AppKit import SwiftUI @MainActor -@NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { private let quitItem: NSMenuItem = { let quitItem = NSMenuItem( @@ -17,7 +16,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { }() private let toggleItem: NSMenuItem = { - let toggleView = NSHostingView(rootView: MenuItemToggleView()) + let toggleView = NSHostingView(rootView: MenuItemToggleView(tunnel: BurrowApp.tunnel)) toggleView.frame.size = CGSize(width: 300, height: 32) toggleView.autoresizingMask = [.width] diff --git a/Apple/App/Assets.xcassets/HackClub.colorset/Contents.json b/Apple/App/Assets.xcassets/HackClub.colorset/Contents.json deleted file mode 100644 index 911b4b1..0000000 --- a/Apple/App/Assets.xcassets/HackClub.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x50", - "green" : "0x37", - "red" : "0xEC" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Apple/App/Assets.xcassets/HackClub.imageset/Contents.json b/Apple/App/Assets.xcassets/HackClub.imageset/Contents.json deleted file mode 100644 index ddd0664..0000000 --- a/Apple/App/Assets.xcassets/HackClub.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "filename" : "flag-standalone-wtransparent.pdf", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Apple/App/Assets.xcassets/HackClub.imageset/flag-standalone-wtransparent.pdf b/Apple/App/Assets.xcassets/HackClub.imageset/flag-standalone-wtransparent.pdf deleted file mode 100644 index 1506fe9..0000000 Binary files a/Apple/App/Assets.xcassets/HackClub.imageset/flag-standalone-wtransparent.pdf and /dev/null differ diff --git a/Apple/App/Assets.xcassets/WireGuard.colorset/Contents.json b/Apple/App/Assets.xcassets/WireGuard.colorset/Contents.json deleted file mode 100644 index 092ec69..0000000 --- a/Apple/App/Assets.xcassets/WireGuard.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x1A", - "green" : "0x17", - "red" : "0x88" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Apple/App/Assets.xcassets/WireGuard.imageset/Contents.json b/Apple/App/Assets.xcassets/WireGuard.imageset/Contents.json deleted file mode 100644 index e7fe15a..0000000 --- a/Apple/App/Assets.xcassets/WireGuard.imageset/Contents.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "images" : [ - { - "filename" : "WireGuard.svg", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "preserves-vector-representation" : true - } -} diff --git a/Apple/App/Assets.xcassets/WireGuard.imageset/WireGuard.svg b/Apple/App/Assets.xcassets/WireGuard.imageset/WireGuard.svg deleted file mode 100644 index 9520f89..0000000 --- a/Apple/App/Assets.xcassets/WireGuard.imageset/WireGuard.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Apple/App/Assets.xcassets/WireGuardTitle.imageset/Contents.json b/Apple/App/Assets.xcassets/WireGuardTitle.imageset/Contents.json deleted file mode 100644 index 782dd12..0000000 --- a/Apple/App/Assets.xcassets/WireGuardTitle.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "WireGuardTitle.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Apple/App/Assets.xcassets/WireGuardTitle.imageset/WireGuardTitle.svg b/Apple/App/Assets.xcassets/WireGuardTitle.imageset/WireGuardTitle.svg deleted file mode 100644 index 64946da..0000000 --- a/Apple/App/Assets.xcassets/WireGuardTitle.imageset/WireGuardTitle.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/Apple/App/BurrowApp.swift b/Apple/App/BurrowApp.swift index 21ebf84..e8aed86 100644 --- a/Apple/App/BurrowApp.swift +++ b/Apple/App/BurrowApp.swift @@ -1,13 +1,21 @@ import SwiftUI -#if !os(macOS) -@MainActor @main +@MainActor struct BurrowApp: App { + static let tunnel = Tunnel { manager, proto in + proto.serverAddress = "hackclub.com" + manager.localizedDescription = "Burrow" + } + + #if os(macOS) + @NSApplicationDelegateAdaptor(AppDelegate.self) + var delegate + #endif + var body: some Scene { WindowGroup { - BurrowView() + TunnelView(tunnel: Self.tunnel) } } } -#endif diff --git a/Apple/App/BurrowView.swift b/Apple/App/BurrowView.swift deleted file mode 100644 index b78b1e1..0000000 --- a/Apple/App/BurrowView.swift +++ /dev/null @@ -1,26 +0,0 @@ -import SwiftUI - -struct BurrowView: View { - var body: some View { - NavigationStack { - VStack { - NetworkCarouselView() - Spacer() - TunnelStatusView() - TunnelButton() - .padding(.bottom) - } - .padding() - .navigationTitle("Networks") - } - } -} - -#if DEBUG -struct NetworkView_Previews: PreviewProvider { - static var previews: some View { - BurrowView() - .environment(\.tunnel, PreviewTunnel()) - } -} -#endif diff --git a/Apple/App/FloatingButtonStyle.swift b/Apple/App/FloatingButtonStyle.swift deleted file mode 100644 index 53ab5ed..0000000 --- a/Apple/App/FloatingButtonStyle.swift +++ /dev/null @@ -1,50 +0,0 @@ -import SwiftUI - -struct FloatingButtonStyle: ButtonStyle { - static let duration = 0.08 - - var color: Color - var cornerRadius: CGFloat - - func makeBody(configuration: Configuration) -> some View { - configuration.label - .font(.headline) - .foregroundColor(.white) - .frame(minHeight: 48) - .padding(.horizontal) - .background( - RoundedRectangle(cornerRadius: cornerRadius) - .fill( - LinearGradient( - colors: [ - configuration.isPressed ? color.opacity(0.9) : color.opacity(0.9), - configuration.isPressed ? color.opacity(0.9) : color - ], - startPoint: .init(x: 0.2, y: 0), - endPoint: .init(x: 0.8, y: 1) - ) - ) - .background( - RoundedRectangle(cornerRadius: cornerRadius) - .fill(configuration.isPressed ? .black : .white) - ) - ) - .shadow(color: .black.opacity(configuration.isPressed ? 0.0 : 0.1), radius: 2.5, x: 0, y: 2) - .scaleEffect(configuration.isPressed ? 0.975 : 1.0) - .padding(.bottom, 2) - .animation( - configuration.isPressed ? .easeOut(duration: Self.duration) : .easeIn(duration: Self.duration), - value: configuration.isPressed - ) - } -} - -extension ButtonStyle where Self == FloatingButtonStyle { - static var floating: FloatingButtonStyle { - floating() - } - - static func floating(color: Color = .accentColor, cornerRadius: CGFloat = 10) -> FloatingButtonStyle { - FloatingButtonStyle(color: color, cornerRadius: cornerRadius) - } -} diff --git a/Apple/App/MainMenu.xib b/Apple/App/MainMenu.xib deleted file mode 100644 index 8933f30..0000000 --- a/Apple/App/MainMenu.xib +++ /dev/null @@ -1,679 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Default - - - - - - - Left to Right - - - - - - - Right to Left - - - - - - - - - - - Default - - - - - - - Left to Right - - - - - - - Right to Left - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Apple/App/Menu/MenuView.swift b/Apple/App/Menu/MenuView.swift new file mode 100644 index 0000000..eab8da2 --- /dev/null +++ b/Apple/App/Menu/MenuView.swift @@ -0,0 +1,60 @@ +// +// MenuView.swift +// App +// +// Created by Thomas Stubblefield on 5/13/23. +// + +import SwiftUI + +struct MenuItemToggleView: View { + var tunnel: Tunnel + + var body: some View { + HStack { + Text("Burrow") + .font(.headline) + Spacer() + Toggle("Burrow", isOn: tunnel.isOn) + .labelsHidden() + .disabled(tunnel.isDisabled) + .toggleStyle(.switch) + } + .padding(.horizontal, 4) + .padding(10) + .frame(minWidth: 300, minHeight: 32, maxHeight: 32) + } +} + +extension Tunnel { + var isDisabled: Bool { + switch self.status { + case .disconnected, .permissionRequired, .connected: + return false + case .unknown, .disabled, .connecting, .reasserting, .disconnecting, .invalid, .configurationReadWriteFailed: + return true + } + } + + var isOn: Binding { + Binding { + switch self.status { + case .connecting, .reasserting, .connected: + true + default: + false + } + } set: { newValue in + switch (self.status, newValue) { + case (.permissionRequired, true): + Task { try await self.configure() } + case (.disconnected, true): + try? self.start() + case (.connected, false): + self.stop() + default: + return + } + } + } +} diff --git a/Apple/App/MenuItemToggleView.swift b/Apple/App/MenuItemToggleView.swift deleted file mode 100644 index 07db51d..0000000 --- a/Apple/App/MenuItemToggleView.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// MenuItemToggleView.swift -// App -// -// Created by Thomas Stubblefield on 5/13/23. -// - -import SwiftUI - -struct MenuItemToggleView: View { - @Environment(\.tunnel) - var tunnel: Tunnel - - var body: some View { - HStack { - VStack(alignment: .leading) { - Text("Burrow") - .font(.headline) - Text(tunnel.status.description) - .font(.subheadline) - } - Spacer() - Toggle(isOn: tunnel.toggleIsOn) { - } - .disabled(tunnel.toggleDisabled) - .toggleStyle(.switch) - } - .accessibilityElement(children: .combine) - .padding(.horizontal, 4) - .padding(10) - .frame(minWidth: 300, minHeight: 32, maxHeight: 32) - } -} - -extension Tunnel { - fileprivate var toggleDisabled: Bool { - switch status { - case .disconnected, .permissionRequired, .connected, .disconnecting: - false - case .unknown, .disabled, .connecting, .reasserting, .invalid, .configurationReadWriteFailed: - true - } - } - - var toggleIsOn: Binding { - Binding { - switch status { - case .connecting, .reasserting, .connected: - true - default: - false - } - } set: { newValue in - switch (status, newValue) { - case (.permissionRequired, true): - enable() - case (_, true): - start() - case (_, false): - stop() - } - } - } -} diff --git a/Apple/App/NetworkExtensionTunnel.swift b/Apple/App/NetworkExtensionTunnel.swift deleted file mode 100644 index 08002de..0000000 --- a/Apple/App/NetworkExtensionTunnel.swift +++ /dev/null @@ -1,167 +0,0 @@ -import BurrowShared -import NetworkExtension - -@Observable -class NetworkExtensionTunnel: Tunnel { - @MainActor private(set) var status: TunnelStatus = .unknown - private var error: NEVPNError? - - private let logger = Logger.logger(for: Tunnel.self) - private let bundleIdentifier: String - private var tasks: [Task] = [] - - // 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 any extra. - private var managers: [NEVPNManager]? { - didSet { Task { await updateStatus() } } - } - - private var currentStatus: TunnelStatus { - guard let managers = managers else { - guard let error = error else { - return .unknown - } - - switch error.code { - case .configurationReadWriteFailed: - return .configurationReadWriteFailed - default: - return .unknown - } - } - - guard let manager = managers.first else { - return .permissionRequired - } - - guard manager.isEnabled else { - return .disabled - } - - return manager.connection.tunnelStatus - } - - convenience init() { - self.init(Constants.networkExtensionBundleIdentifier) - } - - init(_ bundleIdentifier: String) { - self.bundleIdentifier = bundleIdentifier - - let center = NotificationCenter.default - let configurationChanged = Task { [weak self] in - for try await _ in center.notifications(named: .NEVPNConfigurationChange).map({ _ in () }) { - await self?.update() - } - } - let statusChanged = Task { [weak self] in - for try await _ in center.notifications(named: .NEVPNStatusDidChange).map({ _ in () }) { - await self?.updateStatus() - } - } - tasks = [configurationChanged, statusChanged] - - Task { await update() } - } - - private func update() async { - do { - managers = try await NETunnelProviderManager.managers - await self.updateStatus() - } catch let vpnError as NEVPNError { - error = vpnError - } catch { - logger.error("Failed to update VPN configurations: \(error)") - } - } - - private func updateStatus() async { - await MainActor.run { - status = currentStatus - } - } - - func configure() async throws { - if managers == nil { - await update() - } - - guard let managers = managers else { return } - - if managers.count > 1 { - try await withThrowingTaskGroup(of: Void.self, returning: Void.self) { group in - for manager in managers.suffix(from: 1) { - group.addTask { try await manager.remove() } - } - try await group.waitForAll() - } - } - - guard managers.isEmpty else { return } - - let manager = NETunnelProviderManager() - manager.localizedDescription = "Burrow" - - let proto = NETunnelProviderProtocol() - proto.providerBundleIdentifier = bundleIdentifier - proto.serverAddress = "hackclub.com" - - manager.protocolConfiguration = proto - try await manager.save() - } - - func start() { - guard let manager = managers?.first else { return } - Task { - do { - if !manager.isEnabled { - manager.isEnabled = true - try await manager.save() - } - try manager.connection.startVPNTunnel() - } catch { - logger.error("Failed to start: \(error)") - } - } - } - - func stop() { - guard let manager = managers?.first else { return } - manager.connection.stopVPNTunnel() - } - - func enable() { - Task { - do { - try await configure() - } catch { - logger.error("Failed to enable: \(error)") - } - } - } - - deinit { - tasks.forEach { $0.cancel() } - } -} - -extension NEVPNConnection { - fileprivate var tunnelStatus: TunnelStatus { - switch status { - case .connected: - .connected(connectedDate!) - case .connecting: - .connecting - case .disconnecting: - .disconnecting - case .disconnected: - .disconnected - case .reasserting: - .reasserting - case .invalid: - .invalid - @unknown default: - .unknown - } - } -} diff --git a/Apple/App/NetworkView.swift b/Apple/App/NetworkView.swift deleted file mode 100644 index 290254c..0000000 --- a/Apple/App/NetworkView.swift +++ /dev/null @@ -1,88 +0,0 @@ -import SwiftUI - -struct NetworkView: View { - var color: Color - var content: () -> Content - - private var gradient: LinearGradient { - LinearGradient( - colors: [ - color.opacity(0.8), - color - ], - startPoint: .init(x: 0.2, y: 0), - endPoint: .init(x: 0.8, y: 1) - ) - } - - var body: some View { - content() - .frame(maxWidth: .infinity, minHeight: 175, maxHeight: 175) - .background( - RoundedRectangle(cornerRadius: 10) - .fill(gradient) - .background( - RoundedRectangle(cornerRadius: 10) - .fill(.white) - ) - ) - .shadow(color: .black.opacity(0.1), radius: 3.0, x: 0, y: 2) - } -} - -struct AddNetworkView: View { - var body: some View { - Text("Add Network") - .frame(maxWidth: .infinity, minHeight: 175, maxHeight: 175) - .background( - RoundedRectangle(cornerRadius: 10) - .stroke(style: .init(lineWidth: 2, dash: [6])) - ) - } -} - -extension NetworkView where Content == AnyView { - init(network: any Network) { - color = network.backgroundColor - content = { AnyView(network.label) } - } -} - -struct NetworkCarouselView: View { - var networks: [any Network] = [ - HackClub(id: "1"), - HackClub(id: "2"), - WireGuard(id: "4"), - HackClub(id: "5"), - ] - - var body: some View { - ScrollView(.horizontal) { - LazyHStack { - ForEach(networks, id: \.id) { network in - NetworkView(network: network) - .containerRelativeFrame(.horizontal, count: 10, span: 7, spacing: 0, alignment: .center) - .scrollTransition(.interactive, axis: .horizontal) { content, phase in - content - .scaleEffect(1.0 - abs(phase.value) * 0.1) - } - } - AddNetworkView() - } - .scrollTargetLayout() - } - .scrollClipDisabled() - .scrollIndicators(.hidden) - .defaultScrollAnchor(.center) - .scrollTargetBehavior(.viewAligned) - .containerRelativeFrame(.horizontal) - } -} - -#if DEBUG -struct NetworkCarouselView_Previews: PreviewProvider { - static var previews: some View { - NetworkCarouselView() - } -} -#endif diff --git a/Apple/App/Networks/HackClub.swift b/Apple/App/Networks/HackClub.swift deleted file mode 100644 index f7df674..0000000 --- a/Apple/App/Networks/HackClub.swift +++ /dev/null @@ -1,23 +0,0 @@ -import SwiftUI - -struct HackClub: Network { - var id: String - var backgroundColor: Color { .init("HackClub") } - - var label: some View { - GeometryReader { reader in - VStack(alignment: .leading) { - Image("HackClub") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(height: reader.size.height / 4) - Spacer() - Text("@conradev") - .foregroundStyle(.white) - .font(.body.monospaced()) - } - .padding() - .frame(maxWidth: .infinity) - } - } -} diff --git a/Apple/App/Networks/Network.swift b/Apple/App/Networks/Network.swift deleted file mode 100644 index d441d24..0000000 --- a/Apple/App/Networks/Network.swift +++ /dev/null @@ -1,10 +0,0 @@ -import SwiftUI - -protocol Network { - associatedtype Label: View - - var id: String { get } - var backgroundColor: Color { get } - - var label: Label { get } -} diff --git a/Apple/App/Networks/WireGuard.swift b/Apple/App/Networks/WireGuard.swift deleted file mode 100644 index 499288a..0000000 --- a/Apple/App/Networks/WireGuard.swift +++ /dev/null @@ -1,30 +0,0 @@ -import SwiftUI - -struct WireGuard: Network { - var id: String - var backgroundColor: Color { .init("WireGuard") } - - var label: some View { - GeometryReader { reader in - VStack(alignment: .leading) { - HStack { - Image("WireGuard") - .resizable() - .aspectRatio(contentMode: .fit) - Image("WireGuardTitle") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: reader.size.width / 2) - Spacer() - } - .frame(maxWidth: .infinity, maxHeight: reader.size.height / 4) - Spacer() - Text("@conradev") - .foregroundStyle(.white) - .font(.body.monospaced()) - } - .padding() - .frame(maxWidth: .infinity) - } - } -} diff --git a/Apple/App/Status.swift b/Apple/App/Status.swift new file mode 100644 index 0000000..c08cdd1 --- /dev/null +++ b/Apple/App/Status.swift @@ -0,0 +1,42 @@ +import Foundation +import NetworkExtension + +extension Tunnel { + enum Status: CustomStringConvertible, Equatable, Hashable { + case unknown + case permissionRequired + case disabled + case connecting + case connected(Date) + case disconnecting + case disconnected + case reasserting + case invalid + case configurationReadWriteFailed + + var description: String { + switch self { + case .unknown: + return "Unknown" + case .permissionRequired: + return "Permission Required" + case .disconnected: + return "Disconnected" + case .disabled: + return "Disabled" + case .connecting: + return "Connecting" + case .connected: + return "Connected" + case .disconnecting: + return "Disconnecting" + case .reasserting: + return "Reasserting" + case .invalid: + return "Invalid" + case .configurationReadWriteFailed: + return "System Error" + } + } + } +} diff --git a/Apple/App/Tunnel.swift b/Apple/App/Tunnel.swift index 8db366f..5542170 100644 --- a/Apple/App/Tunnel.swift +++ b/Apple/App/Tunnel.swift @@ -1,50 +1,146 @@ +import BurrowShared +import NetworkExtension import SwiftUI -protocol Tunnel { - var status: TunnelStatus { get } - - func start() - func stop() - func enable() -} - -enum TunnelStatus: Equatable, Hashable { - case unknown - case permissionRequired - case disabled - case connecting - case connected(Date) - case disconnecting - case disconnected - case reasserting - case invalid - case configurationReadWriteFailed -} - -struct TunnelKey: EnvironmentKey { - static let defaultValue: any Tunnel = NetworkExtensionTunnel() -} - -extension EnvironmentValues { - var tunnel: any Tunnel { - get { self[TunnelKey.self] } - set { self[TunnelKey.self] = newValue } - } -} - -#if DEBUG @Observable -class PreviewTunnel: Tunnel { - var status: TunnelStatus = .permissionRequired +class Tunnel { + private(set) var status: Status = .unknown + private var error: NEVPNError? - func start() { - status = .connected(.now) + private let logger = Logger.logger(for: Tunnel.self) + private let bundleIdentifier: String + private let configure: (NETunnelProviderManager, NETunnelProviderProtocol) -> Void + private var tasks: [Task] = [] + + // 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 } } + + private var currentStatus: Status { + guard let managers = managers else { + guard let error = error else { + return .unknown + } + + switch error.code { + case .configurationReadWriteFailed: + return .configurationReadWriteFailed + default: + return .unknown + } + } + + guard let manager = managers.first else { + return .permissionRequired + } + + guard manager.isEnabled else { + return .disabled + } + + return manager.connection.tunnelStatus + } + + convenience init(configure: @escaping (NETunnelProviderManager, NETunnelProviderProtocol) -> Void) { + self.init("com.hackclub.burrow.network", configure: configure) + } + + init(_ bundleIdentifier: String, configure: @escaping (NETunnelProviderManager, NETunnelProviderProtocol) -> Void) { + self.bundleIdentifier = bundleIdentifier + self.configure = configure + + let center = NotificationCenter.default + let configurationChanged = Task { + for try await _ in center.notifications(named: .NEVPNConfigurationChange).map({ _ in () }) { + await update() + } + } + let statusChanged = Task { + for try await _ in center.notifications(named: .NEVPNStatusDidChange).map({ _ in () }) { + await MainActor.run { + status = currentStatus + } + } + } + tasks = [configurationChanged, statusChanged] + + Task { await update() } + } + + private func update() async { + do { + let updated = try await NETunnelProviderManager.managers + await MainActor.run { + managers = updated + } + } catch let vpnError as NEVPNError { + error = vpnError + } catch { + logger.error("Failed to update VPN configurations: \(error)") + } + } + + func configure() async throws { + if managers == nil { + await update() + } + + guard let managers = managers else { return } + + if managers.count > 1 { + try await withThrowingTaskGroup(of: Void.self, returning: Void.self) { group in + for manager in managers.suffix(from: 1) { + group.addTask { try await manager.remove() } + } + try await group.waitForAll() + } + } + + if managers.isEmpty { + let manager = NETunnelProviderManager() + let proto = NETunnelProviderProtocol() + proto.providerBundleIdentifier = bundleIdentifier + configure(manager, proto) + + manager.protocolConfiguration = proto + try await manager.save() + } + } + + func start() throws { + guard let manager = managers?.first else { return } + try manager.connection.startVPNTunnel() + } + func stop() { - status = .disconnected + guard let manager = managers?.first else { return } + manager.connection.stopVPNTunnel() } - func enable() { - status = .disconnected + + deinit { + tasks.forEach { $0.cancel() } + } +} + +extension NEVPNConnection { + var tunnelStatus: Tunnel.Status { + switch status { + case .connected: + .connected(connectedDate!) + case .connecting: + .connecting + case .disconnecting: + .disconnecting + case .disconnected: + .disconnected + case .reasserting: + .reasserting + case .invalid: + .invalid + @unknown default: + .unknown + } } } -#endif diff --git a/Apple/App/TunnelButton.swift b/Apple/App/TunnelButton.swift deleted file mode 100644 index df8d7e6..0000000 --- a/Apple/App/TunnelButton.swift +++ /dev/null @@ -1,61 +0,0 @@ -import SwiftUI - -struct TunnelButton: View { - @Environment(\.tunnel) - var tunnel: any Tunnel - - var body: some View { - if let action = tunnel.action { - Button { - tunnel.perform(action) - } label: { - Text(action.description) - } - .padding(.horizontal) - .buttonStyle(.floating) - } - } -} - -extension Tunnel { - fileprivate var action: TunnelButton.Action? { - switch status { - case .permissionRequired, .invalid: - .enable - case .disabled, .disconnecting, .disconnected: - .start - case .connecting, .connected, .reasserting: - .stop - case .unknown, .configurationReadWriteFailed: - nil - } - } -} - -extension TunnelButton { - fileprivate enum Action { - case enable - case start - case stop - } -} - -extension TunnelButton.Action { - var description: LocalizedStringKey { - switch self { - case .enable: "Enable" - case .start: "Start" - case .stop: "Stop" - } - } -} - -extension Tunnel { - fileprivate func perform(_ action: TunnelButton.Action) { - switch action { - case .enable: enable() - case .start: start() - case .stop: stop() - } - } -} diff --git a/Apple/App/TunnelStatusView.swift b/Apple/App/TunnelStatusView.swift deleted file mode 100644 index 3593516..0000000 --- a/Apple/App/TunnelStatusView.swift +++ /dev/null @@ -1,37 +0,0 @@ -import SwiftUI - -struct TunnelStatusView: View { - @Environment(\.tunnel) - var tunnel: any Tunnel - - var body: some View { - Text(tunnel.status.description) - } -} - -extension TunnelStatus: CustomStringConvertible { - var description: String { - switch self { - case .unknown: - "Unknown" - case .permissionRequired: - "Permission Required" - case .disconnected: - "Disconnected" - case .disabled: - "Disabled" - case .connecting: - "Connecting…" - case .connected: - "Connected" - case .disconnecting: - "Disconnecting…" - case .reasserting: - "Reasserting…" - case .invalid: - "Invalid" - case .configurationReadWriteFailed: - "System Error" - } - } -} diff --git a/Apple/App/TunnelView.swift b/Apple/App/TunnelView.swift new file mode 100644 index 0000000..dd91603 --- /dev/null +++ b/Apple/App/TunnelView.swift @@ -0,0 +1,34 @@ +import SwiftUI + +struct TunnelView: View { + 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() + } + } + .padding() + } + + private func start() { + try? tunnel.start() + } + + private func stop() { + tunnel.stop() + } + + private func configure() { + Task { try await tunnel.configure() } + } +} diff --git a/Apple/Burrow.xcodeproj/project.pbxproj b/Apple/Burrow.xcodeproj/project.pbxproj index 8717a30..428d9ab 100644 --- a/Apple/Burrow.xcodeproj/project.pbxproj +++ b/Apple/Burrow.xcodeproj/project.pbxproj @@ -9,32 +9,24 @@ /* Begin PBXBuildFile section */ 0B28F1562ABF463A000D44B0 /* DataTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B28F1552ABF463A000D44B0 /* DataTypes.swift */; }; 0B46E8E02AC918CA00BA2A3C /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B46E8DF2AC918CA00BA2A3C /* Client.swift */; }; - 43AA26D82A10004900F14CE6 /* MenuItemToggleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43AA26D72A10004900F14CE6 /* MenuItemToggleView.swift */; }; + 43AA26D82A10004900F14CE6 /* MenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43AA26D72A10004900F14CE6 /* MenuView.swift */; }; D00117312B2FFFC900D87C25 /* NWConnection+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00117302B2FFFC900D87C25 /* NWConnection+Async.swift */; }; D00117332B3001A400D87C25 /* NewlineProtocolFramer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00117322B3001A400D87C25 /* NewlineProtocolFramer.swift */; }; D001173B2B30341C00D87C25 /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D001173A2B30341C00D87C25 /* Logging.swift */; }; D00117442B30372900D87C25 /* libBurrowShared.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D00117382B30341C00D87C25 /* libBurrowShared.a */; }; D00117452B30372C00D87C25 /* libBurrowShared.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D00117382B30341C00D87C25 /* libBurrowShared.a */; }; D00AA8972A4669BC005C8102 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00AA8962A4669BC005C8102 /* AppDelegate.swift */; }; - D01A79312B81630D0024EC91 /* NetworkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01A79302B81630D0024EC91 /* NetworkView.swift */; }; D020F65829E4A697002790F6 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D020F65729E4A697002790F6 /* PacketTunnelProvider.swift */; }; D020F65D29E4A697002790F6 /* BurrowNetworkExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D020F65329E4A697002790F6 /* BurrowNetworkExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - D032E6522B8A79C20006B8AD /* HackClub.swift in Sources */ = {isa = PBXBuildFile; fileRef = D032E6512B8A79C20006B8AD /* HackClub.swift */; }; - D032E6542B8A79DA0006B8AD /* WireGuard.swift in Sources */ = {isa = PBXBuildFile; fileRef = D032E6532B8A79DA0006B8AD /* WireGuard.swift */; }; D05B9F7629E39EEC008CB1F9 /* BurrowApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */; }; - D05B9F7829E39EEC008CB1F9 /* BurrowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05B9F7729E39EEC008CB1F9 /* BurrowView.swift */; }; + D05B9F7829E39EEC008CB1F9 /* TunnelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05B9F7729E39EEC008CB1F9 /* TunnelView.swift */; }; D05B9F7A29E39EED008CB1F9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D05B9F7929E39EED008CB1F9 /* Assets.xcassets */; }; - D05EF8C82B81818D0017AB4F /* FloatingButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05EF8C72B81818D0017AB4F /* FloatingButtonStyle.swift */; }; D08252762B5C9FC4005DA378 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08252752B5C9FC4005DA378 /* Constants.swift */; }; - D09150422B9D2AF700BE3CB0 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = D09150412B9D2AF700BE3CB0 /* MainMenu.xib */; }; D0BCC5FD2A086D4700AD070D /* NetworkExtension+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */; }; + D0BCC5FF2A086E1C00AD070D /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BCC5FE2A086E1C00AD070D /* Status.swift */; }; D0BCC6082A0981FE00AD070D /* Tunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B98FC629FDC5B5004E7149 /* Tunnel.swift */; }; D0BCC6092A09A03E00AD070D /* libburrow.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0BCC6032A09535900AD070D /* libburrow.a */; }; D0BCC60A2A09A0B800AD070D /* build-rust.sh in Resources */ = {isa = PBXBuildFile; fileRef = D0B98FBF29FD8072004E7149 /* build-rust.sh */; }; - D0FAB5922B818A5900F6A84B /* NetworkExtensionTunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FAB5912B818A5900F6A84B /* NetworkExtensionTunnel.swift */; }; - D0FAB5962B818B2900F6A84B /* TunnelButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FAB5952B818B2900F6A84B /* TunnelButton.swift */; }; - D0FAB5982B818B8200F6A84B /* TunnelStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FAB5972B818B8200F6A84B /* TunnelStatusView.swift */; }; - D0FAB59A2B818B9600F6A84B /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FAB5992B818B9600F6A84B /* Network.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -78,7 +70,7 @@ /* Begin PBXFileReference section */ 0B28F1552ABF463A000D44B0 /* DataTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataTypes.swift; sourceTree = ""; }; 0B46E8DF2AC918CA00BA2A3C /* Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = ""; }; - 43AA26D72A10004900F14CE6 /* MenuItemToggleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuItemToggleView.swift; sourceTree = ""; }; + 43AA26D72A10004900F14CE6 /* MenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuView.swift; sourceTree = ""; }; D00117302B2FFFC900D87C25 /* NWConnection+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NWConnection+Async.swift"; sourceTree = ""; }; D00117322B3001A400D87C25 /* NewlineProtocolFramer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewlineProtocolFramer.swift; sourceTree = ""; }; D00117382B30341C00D87C25 /* libBurrowShared.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libBurrowShared.a; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -86,7 +78,6 @@ D00117412B30347800D87C25 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; D00117422B30348D00D87C25 /* Shared.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = ""; }; D00AA8962A4669BC005C8102 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - D01A79302B81630D0024EC91 /* NetworkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkView.swift; sourceTree = ""; }; D020F63D29E4A1FF002790F6 /* Identity.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Identity.xcconfig; sourceTree = ""; }; D020F64029E4A1FF002790F6 /* Compiler.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Compiler.xcconfig; sourceTree = ""; }; D020F64229E4A1FF002790F6 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -101,26 +92,19 @@ D020F66729E4A95D002790F6 /* NetworkExtension-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "NetworkExtension-iOS.entitlements"; sourceTree = ""; }; D020F66829E4AA74002790F6 /* App-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "App-iOS.entitlements"; sourceTree = ""; }; D020F66929E4AA74002790F6 /* App-macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "App-macOS.entitlements"; sourceTree = ""; }; - D032E6512B8A79C20006B8AD /* HackClub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HackClub.swift; sourceTree = ""; }; - D032E6532B8A79DA0006B8AD /* WireGuard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WireGuard.swift; sourceTree = ""; }; D05B9F7229E39EEC008CB1F9 /* Burrow.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Burrow.app; sourceTree = BUILT_PRODUCTS_DIR; }; D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurrowApp.swift; sourceTree = ""; }; - D05B9F7729E39EEC008CB1F9 /* BurrowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurrowView.swift; sourceTree = ""; }; + D05B9F7729E39EEC008CB1F9 /* TunnelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelView.swift; sourceTree = ""; }; D05B9F7929E39EED008CB1F9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - D05EF8C72B81818D0017AB4F /* FloatingButtonStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FloatingButtonStyle.swift; sourceTree = ""; }; D08252742B5C9DEB005DA378 /* Constants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Constants.h; sourceTree = ""; }; D08252752B5C9FC4005DA378 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; - D09150412B9D2AF700BE3CB0 /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = ""; }; D0B98FBF29FD8072004E7149 /* build-rust.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "build-rust.sh"; sourceTree = ""; }; D0B98FC629FDC5B5004E7149 /* Tunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tunnel.swift; sourceTree = ""; }; D0B98FD829FDDB6F004E7149 /* libburrow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = libburrow.h; sourceTree = ""; }; D0B98FDC29FDDDCF004E7149 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NetworkExtension+Async.swift"; sourceTree = ""; }; + D0BCC5FE2A086E1C00AD070D /* Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Status.swift; sourceTree = ""; }; D0BCC6032A09535900AD070D /* libburrow.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libburrow.a; sourceTree = BUILT_PRODUCTS_DIR; }; - D0FAB5912B818A5900F6A84B /* NetworkExtensionTunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkExtensionTunnel.swift; sourceTree = ""; }; - D0FAB5952B818B2900F6A84B /* TunnelButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelButton.swift; sourceTree = ""; }; - D0FAB5972B818B8200F6A84B /* TunnelStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelStatusView.swift; sourceTree = ""; }; - D0FAB5992B818B9600F6A84B /* Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -151,6 +135,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 43AA26D62A0FFFD000F14CE6 /* Menu */ = { + isa = PBXGroup; + children = ( + 43AA26D72A10004900F14CE6 /* MenuView.swift */, + ); + path = Menu; + sourceTree = ""; + }; D00117392B30341C00D87C25 /* Shared */ = { isa = PBXGroup; children = ( @@ -207,16 +199,6 @@ path = NetworkExtension; sourceTree = ""; }; - D032E64D2B8A69C90006B8AD /* Networks */ = { - isa = PBXGroup; - children = ( - D0FAB5992B818B9600F6A84B /* Network.swift */, - D032E6512B8A79C20006B8AD /* HackClub.swift */, - D032E6532B8A79DA0006B8AD /* WireGuard.swift */, - ); - path = Networks; - sourceTree = ""; - }; D05B9F6929E39EEC008CB1F9 = { isa = PBXGroup; children = ( @@ -242,20 +224,14 @@ D05B9F7429E39EEC008CB1F9 /* App */ = { isa = PBXGroup; children = ( + 43AA26D62A0FFFD000F14CE6 /* Menu */, D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */, D00AA8962A4669BC005C8102 /* AppDelegate.swift */, - 43AA26D72A10004900F14CE6 /* MenuItemToggleView.swift */, - D05B9F7729E39EEC008CB1F9 /* BurrowView.swift */, - D01A79302B81630D0024EC91 /* NetworkView.swift */, - D032E64D2B8A69C90006B8AD /* Networks */, - D0FAB5972B818B8200F6A84B /* TunnelStatusView.swift */, - D0FAB5952B818B2900F6A84B /* TunnelButton.swift */, + D05B9F7729E39EEC008CB1F9 /* TunnelView.swift */, D0B98FC629FDC5B5004E7149 /* Tunnel.swift */, - D0FAB5912B818A5900F6A84B /* NetworkExtensionTunnel.swift */, + D0BCC5FE2A086E1C00AD070D /* Status.swift */, D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */, - D05EF8C72B81818D0017AB4F /* FloatingButtonStyle.swift */, D05B9F7929E39EED008CB1F9 /* Assets.xcassets */, - D09150412B9D2AF700BE3CB0 /* MainMenu.xib */, D020F66829E4AA74002790F6 /* App-iOS.entitlements */, D020F66929E4AA74002790F6 /* App-macOS.entitlements */, D020F64929E4A34B002790F6 /* App.xcconfig */, @@ -343,7 +319,7 @@ attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1510; - LastUpgradeCheck = 1520; + LastUpgradeCheck = 1430; TargetAttributes = { D00117372B30341C00D87C25 = { CreatedOnToolsVersion = 15.1; @@ -393,7 +369,6 @@ buildActionMask = 2147483647; files = ( D05B9F7A29E39EED008CB1F9 /* Assets.xcassets in Resources */, - D09150422B9D2AF700BE3CB0 /* MainMenu.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -448,19 +423,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D0FAB59A2B818B9600F6A84B /* Network.swift in Sources */, D0BCC6082A0981FE00AD070D /* Tunnel.swift in Sources */, - D0FAB5982B818B8200F6A84B /* TunnelStatusView.swift in Sources */, - 43AA26D82A10004900F14CE6 /* MenuItemToggleView.swift in Sources */, - D05B9F7829E39EEC008CB1F9 /* BurrowView.swift in Sources */, - D0FAB5922B818A5900F6A84B /* NetworkExtensionTunnel.swift in Sources */, - D0FAB5962B818B2900F6A84B /* TunnelButton.swift in Sources */, + 43AA26D82A10004900F14CE6 /* MenuView.swift in Sources */, + D05B9F7829E39EEC008CB1F9 /* TunnelView.swift in Sources */, + D0BCC5FF2A086E1C00AD070D /* Status.swift in Sources */, D00AA8972A4669BC005C8102 /* AppDelegate.swift in Sources */, - D05EF8C82B81818D0017AB4F /* FloatingButtonStyle.swift in Sources */, - D032E6522B8A79C20006B8AD /* HackClub.swift in Sources */, D05B9F7629E39EEC008CB1F9 /* BurrowApp.swift in Sources */, - D01A79312B81630D0024EC91 /* NetworkView.swift in Sources */, - D032E6542B8A79DA0006B8AD /* WireGuard.swift in Sources */, D0BCC5FD2A086D4700AD070D /* NetworkExtension+Async.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -600,8 +568,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/realm/SwiftLint.git"; requirement = { - branch = main; - kind = branch; + kind = upToNextMajorVersion; + minimumVersion = 0.54.0; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/Apple/Burrow.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Apple/Burrow.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 9378372..7522840 100644 --- a/Apple/Burrow.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Apple/Burrow.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-argument-parser.git", "state" : { - "revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531", - "version" : "1.2.3" + "revision" : "fee6933f37fde9a5e12a1e4aeaa93fe60116ff2a", + "version" : "1.2.2" } }, { @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-syntax.git", "state" : { - "revision" : "64889f0c732f210a935a0ad7cda38f77f876262d", - "version" : "509.1.1" + "revision" : "6ad4ea24b01559dde0773e3d091f1b9e36175036", + "version" : "509.0.2" } }, { @@ -50,8 +50,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/realm/SwiftLint.git", "state" : { - "branch" : "main", - "revision" : "7595ad3fafc1a31086dc40ba01fd898bf6b42d5f" + "revision" : "f17a4f9dfb6a6afb0408426354e4180daaf49cee", + "version" : "0.54.0" } }, { @@ -68,8 +68,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/drmohundro/SWXMLHash.git", "state" : { - "revision" : "a853604c9e9a83ad9954c7e3d2a565273982471f", - "version" : "7.0.2" + "revision" : "4d0f62f561458cbe1f732171e625f03195151b60", + "version" : "7.0.1" } }, { diff --git a/Apple/Burrow.xcodeproj/xcshareddata/xcschemes/App.xcscheme b/Apple/Burrow.xcodeproj/xcshareddata/xcschemes/App.xcscheme index 670823d..c63f8e6 100644 --- a/Apple/Burrow.xcodeproj/xcshareddata/xcschemes/App.xcscheme +++ b/Apple/Burrow.xcodeproj/xcshareddata/xcschemes/App.xcscheme @@ -1,6 +1,6 @@