From 291b12b78530d9667addc95803cbe568947d3c70 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 6 Jun 2023 22:09:27 +0100 Subject: [PATCH] Some SwiftUI --- .../ConfigurationProxy.swift | 6 +- .../CrossPlatformAppDelegate.swift | 178 +++++++++----- .../NSNotification.swift | 0 .../Application/AppDelegate.swift | 6 - .../TableViewController.swift | 6 +- Yggdrasil Network.xcodeproj/project.pbxproj | 231 ++++++++++++++++-- YggdrasilSwiftUI/Application.swift | 63 +++++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon 1.appiconset/Contents.json | 104 ++++++++ .../AppIcon 1.appiconset/drawing copy-1.png | Bin 0 -> 2734 bytes .../AppIcon 1.appiconset/drawing copy-2.png | Bin 0 -> 1755 bytes .../AppIcon 1.appiconset/drawing copy-3.png | Bin 0 -> 3487 bytes .../AppIcon 1.appiconset/drawing copy-4.png | Bin 0 -> 26573 bytes .../AppIcon 1.appiconset/drawing copy-5.png | Bin 0 -> 3815 bytes .../AppIcon 1.appiconset/drawing copy.png | Bin 0 -> 4132 bytes .../AppIcon.appiconset/Contents.json | 65 +++++ .../AppIcon.appiconset/drawing copy-4 1.png | Bin 0 -> 26573 bytes .../AppIcon.appiconset/drawing copy-4.png | Bin 0 -> 26573 bytes .../Assets.xcassets/Contents.json | 6 + YggdrasilSwiftUI/PeersView.swift | 77 ++++++ .../Preview Assets.xcassets/Contents.json | 6 + YggdrasilSwiftUI/SettingsView.swift | 84 +++++++ YggdrasilSwiftUI/StatusView.swift | 125 ++++++++++ .../YggdrasilSwiftUI.entitlements | 22 ++ 24 files changed, 901 insertions(+), 89 deletions(-) rename {Yggdrasil Network iOS/Application => Yggdrasil Network Cross-Platform}/NSNotification.swift (100%) create mode 100644 YggdrasilSwiftUI/Application.swift create mode 100644 YggdrasilSwiftUI/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/Contents.json create mode 100644 YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy-1.png create mode 100644 YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy-2.png create mode 100644 YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy-3.png create mode 100644 YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy-4.png create mode 100644 YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy-5.png create mode 100644 YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy.png create mode 100644 YggdrasilSwiftUI/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 YggdrasilSwiftUI/Assets.xcassets/AppIcon.appiconset/drawing copy-4 1.png create mode 100644 YggdrasilSwiftUI/Assets.xcassets/AppIcon.appiconset/drawing copy-4.png create mode 100644 YggdrasilSwiftUI/Assets.xcassets/Contents.json create mode 100644 YggdrasilSwiftUI/PeersView.swift create mode 100644 YggdrasilSwiftUI/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 YggdrasilSwiftUI/SettingsView.swift create mode 100644 YggdrasilSwiftUI/StatusView.swift create mode 100644 YggdrasilSwiftUI/YggdrasilSwiftUI.entitlements diff --git a/Yggdrasil Network Cross-Platform/ConfigurationProxy.swift b/Yggdrasil Network Cross-Platform/ConfigurationProxy.swift index de0ad86..e75696b 100644 --- a/Yggdrasil Network Cross-Platform/ConfigurationProxy.swift +++ b/Yggdrasil Network Cross-Platform/ConfigurationProxy.swift @@ -71,7 +71,7 @@ class ConfigurationProxy: PlatformItemSource { "Regex": "en.*", "Beacon": true, "Listen": true, - ] + ] as [String : Any] ]) } } @@ -185,11 +185,7 @@ class ConfigurationProxy: PlatformItemSource { self.fix() if let data = self.data() { let providerProtocol = NETunnelProviderProtocol() - #if os(iOS) providerProtocol.providerBundleIdentifier = "eu.neilalexander.yggdrasil.extension" - #elseif os(OSX) - providerProtocol.providerBundleIdentifier = "eu.neilalexander.yggdrasilmac.extension" - #endif providerProtocol.providerConfiguration = [ "json": data ] providerProtocol.serverAddress = "yggdrasil" providerProtocol.username = self.get("PublicKey") as? String ?? self.get("SigningPublicKey") as? String ?? "(unknown public key)" diff --git a/Yggdrasil Network Cross-Platform/CrossPlatformAppDelegate.swift b/Yggdrasil Network Cross-Platform/CrossPlatformAppDelegate.swift index 1e499d4..69f29f6 100644 --- a/Yggdrasil Network Cross-Platform/CrossPlatformAppDelegate.swift +++ b/Yggdrasil Network Cross-Platform/CrossPlatformAppDelegate.swift @@ -8,45 +8,86 @@ import Foundation import NetworkExtension import Yggdrasil -import UIKit +import SwiftUI -class CrossPlatformAppDelegate: PlatformAppDelegate { +#if os(iOS) +class PlatformAppDelegate: UIResponder, UIApplicationDelegate {} +typealias PlatformApplication = UIApplication +typealias ApplicationDelegateAdaptor = UIApplicationDelegateAdaptor +#elseif os(macOS) +class PlatformAppDelegate: NSObject, NSApplicationDelegate {} +typealias PlatformApplication = NSApplication +typealias ApplicationDelegateAdaptor = NSApplicationDelegateAdaptor +#endif + +class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject { var vpnManager: NETunnelProviderManager = NETunnelProviderManager() - - #if os(iOS) let yggdrasilComponent = "eu.neilalexander.yggdrasil.extension" - #elseif os(OSX) - let yggdrasilComponent = "eu.neilalexander.yggdrasilmac.extension" - #endif - var yggdrasilConfig: ConfigurationProxy? = nil - - var yggdrasilAdminTimer: DispatchSourceTimer? - - var yggdrasilSelfIP: String = "N/A" - var yggdrasilSelfSubnet: String = "N/A" - var yggdrasilSelfCoords: String = "[]" - - var yggdrasilPeers: [[String: Any]] = [[:]] - var yggdrasilDHT: [[String: Any]] = [[:]] - var yggdrasilNodeInfo: [String: Any] = [:] - - func applicationDidBecomeActive(_ application: UIApplication) { - if self.yggdrasilAdminTimer == nil { - self.yggdrasilAdminTimer = DispatchSource.makeTimerSource(flags: .strict, queue: DispatchQueue(label: "Admin Queue")) - self.yggdrasilAdminTimer!.schedule(deadline: DispatchTime.now(), repeating: DispatchTimeInterval.seconds(2), leeway: DispatchTimeInterval.seconds(1)) - self.yggdrasilAdminTimer!.setEventHandler { - self.makeIPCRequests() - } - } - if self.yggdrasilAdminTimer != nil { - self.yggdrasilAdminTimer!.resume() - } + override init() { + super.init() + NotificationCenter.default.addObserver(forName: .NEVPNStatusDidChange, object: nil, queue: nil, using: { notification in if let conn = notification.object as? NEVPNConnection { self.updateStatus(conn: conn) } }) + + self.vpnTunnelProviderManagerInit() + self.makeIPCRequests() + } + + func toggleYggdrasil() { + if !self.yggdrasilEnabled { + print("Starting VPN tunnel") + do { + try self.vpnManager.connection.startVPNTunnel() + } catch { + print("Failed to start VPN tunnel: \(error.localizedDescription)") + return + } + print("Started VPN tunnel") + } else { + print("Stopping VPN tunnel") + self.vpnManager.connection.stopVPNTunnel() + print("Stopped VPN tunnel") + } + self.yggdrasilEnabled = !self.yggdrasilEnabled + } + + var yggdrasilConfig: ConfigurationProxy? = nil + + private var adminTimer: DispatchSourceTimer? + + @Published var yggdrasilEnabled: Bool = false + @Published var yggdrasilConnected: Bool = false + + @Published var yggdrasilIP: String = "N/A" + @Published var yggdrasilSubnet: String = "N/A" + @Published var yggdrasilCoords: String = "[]" + + @Published var yggdrasilPeers: [[String: Any]] = [[:]] + @Published var yggdrasilDHT: [[String: Any]] = [[:]] + @Published var yggdrasilNodeInfo: [String: Any] = [:] + + func yggdrasilVersion() -> String { + return Yggdrasil.MobileGetVersion() + } + + func applicationDidBecomeActive(_ application: PlatformApplication) { + print("Application became active") + + if self.adminTimer == nil { + self.adminTimer = DispatchSource.makeTimerSource(flags: .strict, queue: DispatchQueue(label: "Admin Queue")) + self.adminTimer!.schedule(deadline: DispatchTime.now(), repeating: DispatchTimeInterval.seconds(2), leeway: DispatchTimeInterval.seconds(1)) + self.adminTimer!.setEventHandler { + self.makeIPCRequests() + } + } + if self.adminTimer != nil { + self.adminTimer!.resume() + } + self.updateStatus(conn: self.vpnManager.connection) } @@ -56,49 +97,70 @@ class CrossPlatformAppDelegate: PlatformAppDelegate { } else if conn.status == .disconnecting || conn.status == .disconnected { self.clearStatus() } + self.yggdrasilConnected = self.yggdrasilEnabled && self.yggdrasilPeers.count > 0 && self.yggdrasilDHT.count > 0 + print("Connection status: \(yggdrasilEnabled), \(yggdrasilConnected)") } - func applicationWillResignActive(_ application: UIApplication) { - if self.yggdrasilAdminTimer != nil { - self.yggdrasilAdminTimer!.suspend() + func applicationWillResignActive(_ application: PlatformApplication) { + if self.adminTimer != nil { + self.adminTimer!.suspend() } } func vpnTunnelProviderManagerInit() { + print("Loading saved managers...") + NETunnelProviderManager.loadAllFromPreferences { (savedManagers: [NETunnelProviderManager]?, error: Error?) in - if let error = error { - print(error) + guard error == nil else { + print("Failed to load VPN managers: \(error?.localizedDescription ?? "(no error)")") + return } - if let savedManagers = savedManagers { - for manager in savedManagers { - if (manager.protocolConfiguration as? NETunnelProviderProtocol)?.providerBundleIdentifier == self.yggdrasilComponent { - print("Found saved VPN Manager") - self.vpnManager = manager - } + guard let savedManagers else { + print("Expected to find saved managers but didn't") + return + } + + print("Found \(savedManagers.count) saved VPN managers") + for manager in savedManagers { + guard let proto = manager.protocolConfiguration as? NETunnelProviderProtocol else { + continue } + guard let identifier = proto.providerBundleIdentifier else { + continue + } + guard identifier == self.yggdrasilComponent else { + continue + } + print("Found saved VPN Manager") + self.vpnManager = manager + break } self.vpnManager.loadFromPreferences(completionHandler: { (error: Error?) in - if let error = error { - print(error) + if error == nil { + if let vpnConfig = self.vpnManager.protocolConfiguration as? NETunnelProviderProtocol, + let confJson = vpnConfig.providerConfiguration!["json"] as? Data { + if let loaded = try? ConfigurationProxy(json: confJson) { + print("Found existing protocol configuration") + self.yggdrasilConfig = loaded + } else { + print("Existing protocol configuration is invalid, ignoring") + } + } } - if let vpnConfig = self.vpnManager.protocolConfiguration as? NETunnelProviderProtocol, - let confJson = vpnConfig.providerConfiguration!["json"] as? Data { - print("Found existing protocol configuration") - self.yggdrasilConfig = try? ConfigurationProxy(json: confJson) - } else { + if self.yggdrasilConfig == nil { print("Generating new protocol configuration") self.yggdrasilConfig = ConfigurationProxy() + + if let config = self.yggdrasilConfig { + try? config.save(to: &self.vpnManager) + } } self.vpnManager.localizedDescription = "Yggdrasil" self.vpnManager.isEnabled = true - - if let config = self.yggdrasilConfig { - try? config.save(to: &self.vpnManager) - } }) } } @@ -110,19 +172,19 @@ class CrossPlatformAppDelegate: PlatformAppDelegate { if let session = self.vpnManager.connection as? NETunnelProviderSession { try? session.sendProviderMessage("address".data(using: .utf8)!) { (address) in if let address = address { - self.yggdrasilSelfIP = String(data: address, encoding: .utf8)! + self.yggdrasilIP = String(data: address, encoding: .utf8)! NotificationCenter.default.post(name: .YggdrasilSelfUpdated, object: nil) } } try? session.sendProviderMessage("subnet".data(using: .utf8)!) { (subnet) in if let subnet = subnet { - self.yggdrasilSelfSubnet = String(data: subnet, encoding: .utf8)! + self.yggdrasilSubnet = String(data: subnet, encoding: .utf8)! NotificationCenter.default.post(name: .YggdrasilSelfUpdated, object: nil) } } try? session.sendProviderMessage("coords".data(using: .utf8)!) { (coords) in if let coords = coords { - self.yggdrasilSelfCoords = String(data: coords, encoding: .utf8)! + self.yggdrasilCoords = String(data: coords, encoding: .utf8)! NotificationCenter.default.post(name: .YggdrasilSelfUpdated, object: nil) } } @@ -146,9 +208,9 @@ class CrossPlatformAppDelegate: PlatformAppDelegate { } func clearStatus() { - self.yggdrasilSelfIP = "N/A" - self.yggdrasilSelfSubnet = "N/A" - self.yggdrasilSelfCoords = "[]" + self.yggdrasilIP = "N/A" + self.yggdrasilSubnet = "N/A" + self.yggdrasilCoords = "[]" self.yggdrasilPeers = [] self.yggdrasilDHT = [] NotificationCenter.default.post(name: .YggdrasilSelfUpdated, object: nil) diff --git a/Yggdrasil Network iOS/Application/NSNotification.swift b/Yggdrasil Network Cross-Platform/NSNotification.swift similarity index 100% rename from Yggdrasil Network iOS/Application/NSNotification.swift rename to Yggdrasil Network Cross-Platform/NSNotification.swift diff --git a/Yggdrasil Network iOS/Application/AppDelegate.swift b/Yggdrasil Network iOS/Application/AppDelegate.swift index 3e85676..afc437d 100644 --- a/Yggdrasil Network iOS/Application/AppDelegate.swift +++ b/Yggdrasil Network iOS/Application/AppDelegate.swift @@ -1,11 +1,5 @@ import UIKit -#if os(iOS) -class PlatformAppDelegate: UIResponder, UIApplicationDelegate {} -#elseif os(OSX) -class PlatformAppDelegate: NSObject, NSApplicationDelegate {} -#endif - @UIApplicationMain class AppDelegate: CrossPlatformAppDelegate { var window: UIWindow? diff --git a/Yggdrasil Network iOS/View Controllers/TableViewController.swift b/Yggdrasil Network iOS/View Controllers/TableViewController.swift index 58870a9..5de6b2a 100644 --- a/Yggdrasil Network iOS/View Controllers/TableViewController.swift +++ b/Yggdrasil Network iOS/View Controllers/TableViewController.swift @@ -94,9 +94,9 @@ class TableViewController: UITableViewController { } @objc func onYggdrasilSelfUpdated(notification: NSNotification) { - statsSelfIP.text = app.yggdrasilSelfIP - statsSelfSubnet.text = app.yggdrasilSelfSubnet - statsSelfCoords.text = app.yggdrasilSelfCoords + statsSelfIP.text = app.yggdrasilIP + statsSelfSubnet.text = app.yggdrasilSubnet + statsSelfCoords.text = app.yggdrasilCoords statsSelfIPCell.layoutSubviews() statsSelfSubnetCell.layoutSubviews() diff --git a/Yggdrasil Network.xcodeproj/project.pbxproj b/Yggdrasil Network.xcodeproj/project.pbxproj index c83c28c..f491495 100644 --- a/Yggdrasil Network.xcodeproj/project.pbxproj +++ b/Yggdrasil Network.xcodeproj/project.pbxproj @@ -20,8 +20,19 @@ 3996AF38270328080070947D /* Yggdrasil.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3996AF37270328080070947D /* Yggdrasil.xcframework */; }; 3996AF39270328080070947D /* Yggdrasil.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3996AF37270328080070947D /* Yggdrasil.xcframework */; }; 39AE88392319C93F0010FFF6 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 39AE88382319C93F0010FFF6 /* NetworkExtension.framework */; }; + 39B51A4A2997062E0059D29D /* PeersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39B51A492997062E0059D29D /* PeersView.swift */; }; + 39B51A4B29994F240059D29D /* ConfigurationProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3952ADB629945AF700B3835D /* ConfigurationProxy.swift */; }; + 39B51A4C29994F350059D29D /* CrossPlatformAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3952ADB929945AFA00B3835D /* CrossPlatformAppDelegate.swift */; }; + 39B51A4D299951D60059D29D /* NSNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CC924B221DEDCE004960DC /* NSNotification.swift */; }; + 39BF9FC62A2F5FA7000E7269 /* PacketTunnelProvider+FileDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39BF9FC52A2F5FA7000E7269 /* PacketTunnelProvider+FileDescriptor.swift */; }; + 39BF9FC72A2F6FFD000E7269 /* Yggdrasil.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3996AF37270328080070947D /* Yggdrasil.xcframework */; }; 39CC924C221DEDCE004960DC /* NSNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CC924B221DEDCE004960DC /* NSNotification.swift */; }; 39CC924D221DEDD3004960DC /* NSNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CC924B221DEDCE004960DC /* NSNotification.swift */; }; + 39F0205E2996CD760093F603 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39F0205D2996CD760093F603 /* Application.swift */; }; + 39F020602996CD760093F603 /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39F0205F2996CD760093F603 /* StatusView.swift */; }; + 39F020622996CD770093F603 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 39F020612996CD770093F603 /* Assets.xcassets */; }; + 39F020662996CD770093F603 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 39F020652996CD770093F603 /* Preview Assets.xcassets */; }; + 39F0206B2996CF260093F603 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39F0206A2996CF260093F603 /* SettingsView.swift */; }; E593CE6F1DF8FC3C00D7265D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E593CE6E1DF8FC3C00D7265D /* AppDelegate.swift */; }; E593CE711DF8FC3C00D7265D /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E593CE701DF8FC3C00D7265D /* TableViewController.swift */; }; E593CE741DF8FC3C00D7265D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E593CE721DF8FC3C00D7265D /* Main.storyboard */; }; @@ -71,7 +82,18 @@ 39682A382225AD15004FB670 /* CopyableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyableLabel.swift; sourceTree = ""; }; 3996AF37270328080070947D /* Yggdrasil.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Yggdrasil.xcframework; sourceTree = ""; }; 39AE88382319C93F0010FFF6 /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; }; + 39B51A492997062E0059D29D /* PeersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeersView.swift; sourceTree = ""; }; + 39BF9FC12A2E9E51000E7269 /* YggdrasilNetworkExtension-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "YggdrasilNetworkExtension-Bridging-Header.h"; sourceTree = ""; }; + 39BF9FC22A2E9E52000E7269 /* PacketTunnelProvider+FileDescriptor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PacketTunnelProvider+FileDescriptor.h"; sourceTree = ""; }; + 39BF9FC52A2F5FA7000E7269 /* PacketTunnelProvider+FileDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PacketTunnelProvider+FileDescriptor.swift"; sourceTree = ""; }; 39CC924B221DEDCE004960DC /* NSNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSNotification.swift; sourceTree = ""; }; + 39F0205B2996CD760093F603 /* YggdrasilSwiftUI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = YggdrasilSwiftUI.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 39F0205D2996CD760093F603 /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = ""; }; + 39F0205F2996CD760093F603 /* StatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusView.swift; sourceTree = ""; }; + 39F020612996CD770093F603 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 39F020632996CD770093F603 /* YggdrasilSwiftUI.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = YggdrasilSwiftUI.entitlements; sourceTree = ""; }; + 39F020652996CD770093F603 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 39F0206A2996CF260093F603 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; E593CE6B1DF8FC3C00D7265D /* YggdrasilNetwork.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = YggdrasilNetwork.app; sourceTree = BUILT_PRODUCTS_DIR; }; E593CE6E1DF8FC3C00D7265D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; E593CE701DF8FC3C00D7265D /* TableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; }; @@ -85,6 +107,14 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 39F020582996CD760093F603 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 39BF9FC72A2F6FFD000E7269 /* Yggdrasil.xcframework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; E593CE681DF8FC3C00D7265D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -137,7 +167,6 @@ isa = PBXGroup; children = ( E593CE6E1DF8FC3C00D7265D /* AppDelegate.swift */, - 39CC924B221DEDCE004960DC /* NSNotification.swift */, ); path = Application; sourceTree = ""; @@ -156,6 +185,7 @@ children = ( 3952ADB929945AFA00B3835D /* CrossPlatformAppDelegate.swift */, 3952ADB629945AF700B3835D /* ConfigurationProxy.swift */, + 39CC924B221DEDCE004960DC /* NSNotification.swift */, ); name = "Yggdrasil Network Core"; path = "Yggdrasil Network Cross-Platform"; @@ -170,6 +200,28 @@ name = Frameworks; sourceTree = ""; }; + 39F0205C2996CD760093F603 /* YggdrasilSwiftUI */ = { + isa = PBXGroup; + children = ( + 39F0205D2996CD760093F603 /* Application.swift */, + 39F0205F2996CD760093F603 /* StatusView.swift */, + 39F0206A2996CF260093F603 /* SettingsView.swift */, + 39B51A492997062E0059D29D /* PeersView.swift */, + 39F020612996CD770093F603 /* Assets.xcassets */, + 39F020632996CD770093F603 /* YggdrasilSwiftUI.entitlements */, + 39F020642996CD770093F603 /* Preview Content */, + ); + path = YggdrasilSwiftUI; + sourceTree = ""; + }; + 39F020642996CD770093F603 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 39F020652996CD770093F603 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; E593CE621DF8FC3C00D7265D = { isa = PBXGroup; children = ( @@ -178,6 +230,7 @@ 3913E99C21DB9B1C001E0EC7 /* YggdrasilNetworkExtension.entitlements */, E593CE981DF905AF00D7265D /* Yggdrasil Network Extension */, E593CE6D1DF8FC3C00D7265D /* Yggdrasil Network iOS */, + 39F0205C2996CD760093F603 /* YggdrasilSwiftUI */, E593CE6C1DF8FC3C00D7265D /* Products */, 399D032221DA775D0016354F /* Frameworks */, ); @@ -188,6 +241,7 @@ children = ( E593CE6B1DF8FC3C00D7265D /* YggdrasilNetwork.app */, E593CE971DF905AF00D7265D /* YggdrasilNetworkExtension.appex */, + 39F0205B2996CD760093F603 /* YggdrasilSwiftUI.app */, ); name = Products; sourceTree = ""; @@ -214,6 +268,7 @@ 391B72A62A2FD90100896278 /* PacketTunnelProvider+FileDescriptor.swift */, 391B72A72A2FD90100896278 /* YggdrasilNetworkExtension-Bridging-Header.h */, E593CE9D1DF905AF00D7265D /* Info.plist */, + 39BF9FC12A2E9E51000E7269 /* YggdrasilNetworkExtension-Bridging-Header.h */, ); path = "Yggdrasil Network Extension"; sourceTree = ""; @@ -221,6 +276,23 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 39F0205A2996CD760093F603 /* YggdrasilSwiftUI */ = { + isa = PBXNativeTarget; + buildConfigurationList = 39F020672996CD770093F603 /* Build configuration list for PBXNativeTarget "YggdrasilSwiftUI" */; + buildPhases = ( + 39F020572996CD760093F603 /* Sources */, + 39F020582996CD760093F603 /* Frameworks */, + 39F020592996CD760093F603 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = YggdrasilSwiftUI; + productName = YggdrasilSwiftUI; + productReference = 39F0205B2996CD760093F603 /* YggdrasilSwiftUI.app */; + productType = "com.apple.product-type.application"; + }; E593CE6A1DF8FC3C00D7265D /* YggdrasilNetwork */ = { isa = PBXNativeTarget; buildConfigurationList = E593CE7D1DF8FC3C00D7265D /* Build configuration list for PBXNativeTarget "YggdrasilNetwork" */; @@ -263,10 +335,13 @@ E593CE631DF8FC3C00D7265D /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1010; + LastSwiftUpdateCheck = 1420; LastUpgradeCheck = 1420; ORGANIZATIONNAME = ""; TargetAttributes = { + 39F0205A2996CD760093F603 = { + CreatedOnToolsVersion = 14.2; + }; E593CE6A1DF8FC3C00D7265D = { CreatedOnToolsVersion = 8.1; LastSwiftMigration = 1030; @@ -287,7 +362,7 @@ }; E593CE961DF905AF00D7265D = { CreatedOnToolsVersion = 8.1; - LastSwiftMigration = 1030; + LastSwiftMigration = 1430; SystemCapabilities = { com.apple.ApplicationGroups.iOS = { enabled = 1; @@ -317,11 +392,21 @@ targets = ( E593CE6A1DF8FC3C00D7265D /* YggdrasilNetwork */, E593CE961DF905AF00D7265D /* YggdrasilNetworkExtension */, + 39F0205A2996CD760093F603 /* YggdrasilSwiftUI */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 39F020592996CD760093F603 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 39F020662996CD770093F603 /* Preview Assets.xcassets in Resources */, + 39F020622996CD770093F603 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; E593CE691DF8FC3C00D7265D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -342,6 +427,20 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 39F020572996CD760093F603 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 39B51A4B29994F240059D29D /* ConfigurationProxy.swift in Sources */, + 39F0206B2996CF260093F603 /* SettingsView.swift in Sources */, + 39B51A4A2997062E0059D29D /* PeersView.swift in Sources */, + 39F020602996CD760093F603 /* StatusView.swift in Sources */, + 39B51A4D299951D60059D29D /* NSNotification.swift in Sources */, + 39B51A4C29994F350059D29D /* CrossPlatformAppDelegate.swift in Sources */, + 39F0205E2996CD760093F603 /* Application.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; E593CE671DF8FC3C00D7265D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -401,6 +500,95 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 39F020682996CD770093F603 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = YggdrasilSwiftUI/YggdrasilSwiftUI.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"YggdrasilSwiftUI/Preview Content\""; + DEVELOPMENT_TEAM = R9AV23TXF2; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 13.0; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = eu.neilalexander.yggdrasil.YggdrasilSwiftUI; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 39F020692996CD770093F603 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = YggdrasilSwiftUI/YggdrasilSwiftUI.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"YggdrasilSwiftUI/Preview Content\""; + DEVELOPMENT_TEAM = R9AV23TXF2; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 13.0; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = eu.neilalexander.yggdrasil.YggdrasilSwiftUI; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; E593CE7B1DF8FC3C00D7265D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -452,8 +640,8 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - MACOSX_DEPLOYMENT_TARGET = 10.12; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + MACOSX_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -510,8 +698,8 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - MACOSX_DEPLOYMENT_TARGET = 10.12; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + MACOSX_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; @@ -539,7 +727,7 @@ "$(PROJECT_DIR)", ); INFOPLIST_FILE = "$(SRCROOT)/Yggdrasil Network iOS/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -573,7 +761,7 @@ "$(PROJECT_DIR)", ); INFOPLIST_FILE = "$(SRCROOT)/Yggdrasil Network iOS/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -592,6 +780,7 @@ E593CEA11DF905AF00D7265D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = YggdrasilNetworkExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; @@ -603,8 +792,7 @@ "$(PROJECT_DIR)", ); INFOPLIST_FILE = "$(SRCROOT)/Yggdrasil Network Extension/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.2; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -614,17 +802,18 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; STRIP_INSTALLED_PRODUCT = NO; - SUPPORTS_MACCATALYST = NO; + SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_OBJC_BRIDGING_HEADER = "Yggdrasil Network Extension/YggdrasilNetworkExtension-Bridging-Header.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,6"; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; E593CEA21DF905AF00D7265D /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = YggdrasilNetworkExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; @@ -636,8 +825,7 @@ "$(PROJECT_DIR)", ); INFOPLIST_FILE = "$(SRCROOT)/Yggdrasil Network Extension/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.2; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -647,17 +835,26 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = "8ce353d5-fd5f-4d5e-b664-8ad294091125"; PROVISIONING_PROFILE_SPECIFIER = ""; - SUPPORTS_MACCATALYST = NO; + SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_OBJC_BRIDGING_HEADER = "Yggdrasil Network Extension/YggdrasilNetworkExtension-Bridging-Header.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,6"; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 39F020672996CD770093F603 /* Build configuration list for PBXNativeTarget "YggdrasilSwiftUI" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 39F020682996CD770093F603 /* Debug */, + 39F020692996CD770093F603 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; E593CE661DF8FC3C00D7265D /* Build configuration list for PBXProject "Yggdrasil Network" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/YggdrasilSwiftUI/Application.swift b/YggdrasilSwiftUI/Application.swift new file mode 100644 index 0000000..98e2330 --- /dev/null +++ b/YggdrasilSwiftUI/Application.swift @@ -0,0 +1,63 @@ +// +// YggdrasilSwiftUIApp.swift +// YggdrasilSwiftUI +// +// Created by Neil on 10/02/2023. +// + +import SwiftUI +import NetworkExtension + +@main +struct Application: App { + #if os(iOS) + @UIApplicationDelegateAdaptor(CrossPlatformAppDelegate.self) static var appDelegate: CrossPlatformAppDelegate + #elseif os(macOS) + @NSApplicationDelegateAdaptor(CrossPlatformAppDelegate.self) static var appDelegate: CrossPlatformAppDelegate + #endif + + @State private var selection: String? = "Status" + @State private var config: ConfigurationProxy = ConfigurationProxy() + + var body: some Scene { + WindowGroup { + NavigationSplitView { + List(selection: $selection) { + NavigationLink(destination: StatusView(yggdrasilConfiguration: $config)) { + HStack { + Image(systemName: "info.circle") + .foregroundColor(.accentColor) + .frame(minWidth: 24) + Text("Status") + } + } + NavigationLink(destination: PeersView()) { + HStack { + Image(systemName: "antenna.radiowaves.left.and.right") + .foregroundColor(.accentColor) + .frame(minWidth: 24) + Text("Peers") + } + } + NavigationLink(destination: SettingsView()) { + HStack { + Image(systemName: "gear") + .foregroundColor(.accentColor) + .frame(minWidth: 24) + Text("Settings") + } + } + } + .listStyle(.sidebar) + .navigationSplitViewColumnWidth(200) + } detail: { + StatusView(yggdrasilConfiguration: $config) + } + .navigationTitle("Yggdrasil") + .navigationSplitViewStyle(.automatic) + } + #if os(macOS) + .windowStyle(.hiddenTitleBar) + #endif + } +} diff --git a/YggdrasilSwiftUI/Assets.xcassets/AccentColor.colorset/Contents.json b/YggdrasilSwiftUI/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/YggdrasilSwiftUI/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/Contents.json b/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/Contents.json new file mode 100644 index 0000000..96c6335 --- /dev/null +++ b/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/Contents.json @@ -0,0 +1,104 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "filename" : "drawing copy-1.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "drawing copy.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "drawing copy-2.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "filename" : "drawing copy-3.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "filename" : "drawing copy-5.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "filename" : "drawing copy-4.png", + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy-1.png b/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy-1.png new file mode 100644 index 0000000000000000000000000000000000000000..eee5a75518207af0d7b982f88780889f5973d8c8 GIT binary patch literal 2734 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 z z&tD^_kx(<#W|Rea9&C%FdUhTxhYP70{5yIP73n8}t|kwW%{DKs0SuGXa(HRtI8>7f z`x_xIIP^(;h4xy!J~M>NX*M?VEf-P`<`AW-+ds8f#?vwUJ$#m0!E6{(4d&(xI#xx| zRJ<5E21Qe;P1!J{nS77L2iR)7JaGb-Qta(BQ4XXb(k<`-ww$A}F}xG~irS2dK`QbE zffpCv$V4WNmqMSKU3TR`x-K%4%aXOt8VNOn7e+tCf=LsqYeD)5m(H1A#8!f=mc#EZ ze~eH%Mr}&PAr<*)?!lk|_`~=oIGva(__t<+ROF{s?!jJ}IEfGDMySou4PEnKh3>ql zv2h|86@Q*MfqzEKRiRocq^pWDs*0qVfv3u*i!=a#y7D>Rikzo5MR&;OX~Ig9+5|(< zpy7|9<5e-Z00wIEC({YEIjiFsn8jQczaKq{Ph;cMrWgkK{$Q6*e( zoZ>STV4x-+0OHv+wFwpBMC=M)9{c0xi}(Z| zRs|?OL{{jNy(h4>GB?r@4I0iR!WfK>;-lyY=1j7?APbO+e7h)Npmtl{wZ&W(c0qzj zTu!ZO8qOxBFc=-h(dgx>Op;atQjtGUyA3t+_3)Td75wDvn@Fk(_5@n7uf7v~zMvr+ z11p+}3&|Orj!)rCVhZOHVJz}A!-gT9i#*uallS`XlczDAUfLe=y~stp7rBTAy9f8x zwc%F(CT#Zx;S@LiotV!qVk|X>@x>S}r{*w{isC{tf($R=22fd5PPVG3%hP}^Ps6p> z^VvnbF?Dvun+s=>cy00wUYk4vNf6NN@L{VffS}!rdb=9|*$JB{t+>CEQxVIi5znSE zpIuz~Q5G@2IFFb`@@{5;OoJ`=UT;sIN2WjtwW?_drD6!Bcp}p*kQ5yHWe4u9YrFPZ z(Nz3vx;hH*s*Mr^P32tOHygWPzagxB5*@>wlB(GKIRvaRJPa540tD=D?9RLPpYR3h zd@n~Q3>W#k0$b4JxL!FC$s}=No@d0Zis2&vV6Zpu^+z)!rrVmEQ#=MHS~WB~e7HTZ zIq&M=>jdy?xRWyL$Pp)AF$QC!+IE^!8$$l=<((s z88bLD0u9x0xkK0wa; z=064Hr+ylU=xhp(-UnS^TuG3$$2`#Rg71z(k zr}b=>!w|ZL$o);-E3V}<4Hpv;J)1Qi?u^$&H<9m^9T=!@TXB7KA&OMitb4QakyI+W z<-xul?8qxByvl_{1iYiF2i-i_{#C?101PK)$i^2R*2bet!C{A|4(*=R&*J_v9^oBb zMd%{(_cnH{dOfFUxRfN^qm+DPlN(?-y42*aHNeJFb4aUZ75JQT_#dgr#H4o#F|_~y N002ovPDHLkV1l#?mjnO+ literal 0 HcmV?d00001 diff --git a/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy-4.png b/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy-4.png new file mode 100644 index 0000000000000000000000000000000000000000..cc8b151ef1b617508ec3816a4fc623e4b3f45251 GIT binary patch literal 26573 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 Jg8t3_{twQoX<7gP literal 0 HcmV?d00001 diff --git a/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy-5.png b/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy-5.png new file mode 100644 index 0000000000000000000000000000000000000000..d658df22e3a49db791a98d67028bf50c66260c83 GIT binary patch literal 3815 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 Jg8t3_{twQoX<7gP literal 0 HcmV?d00001 diff --git a/YggdrasilSwiftUI/Assets.xcassets/AppIcon.appiconset/drawing copy-4.png b/YggdrasilSwiftUI/Assets.xcassets/AppIcon.appiconset/drawing copy-4.png new file mode 100644 index 0000000000000000000000000000000000000000..cc8b151ef1b617508ec3816a4fc623e4b3f45251 GIT binary patch literal 26573 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 Jg8t3_{twQoX<7gP literal 0 HcmV?d00001 diff --git a/YggdrasilSwiftUI/Assets.xcassets/Contents.json b/YggdrasilSwiftUI/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/YggdrasilSwiftUI/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/YggdrasilSwiftUI/PeersView.swift b/YggdrasilSwiftUI/PeersView.swift new file mode 100644 index 0000000..4bddbc5 --- /dev/null +++ b/YggdrasilSwiftUI/PeersView.swift @@ -0,0 +1,77 @@ +// +// StatisticsView.swift +// YggdrasilSwiftUI +// +// Created by Neil on 10/02/2023. +// + +import SwiftUI + +struct PeersView: View { + @State private var peers = ["Paul", "Taylor", "Adele"] + + @State private var multicastAdvertise = false + @State private var multicastListen = false + + var body: some View { + Form { + Section(content: { + ForEach(peers, id: \.self) { peer in + Text(peer) + } + .onDelete(perform: delete) + Text("Yggdrasil will automatically attempt to connect to configured peers when started. If you configure more than one peer, your device may carry traffic on behalf of other network nodes. Avoid this by configuring only a single peer. Data charges may apply when using mobile data.") + .font(.system(size: 11)) + .foregroundColor(.gray) + }, header: { + Text("Configured peers") + }) + + Section(content: { + Toggle(isOn: $multicastAdvertise) { + VStack(alignment: .leading) { + Text("Discoverable over multicast") + Text("Make your device discoverable to other Yggdrasil nodes on the same Wi-Fi network.") + .font(.system(size: 11)) + .foregroundColor(.gray) + } + } + Toggle(isOn: $multicastListen) { + VStack(alignment: .leading) { + Text("Search for multicast peers") + Text("Automatically connect to discoverable Yggdrasil nodes on the same Wi-Fi network.") + .font(.system(size: 11)) + .foregroundColor(.gray) + } + } + }, header: { + Text("Local connectivity") + }) + } + #if os(iOS) + .toolbar { + Button(role: nil, action: { + + }, label: { + Image(systemName: "plus") + }) + EditButton() + } + #endif + .formStyle(.grouped) + .navigationTitle("Peers") + #if os(iOS) + .navigationBarTitleDisplayMode(.large) + #endif + } + + func delete(at offsets: IndexSet) { + peers.remove(atOffsets: offsets) + } +} + +struct PeersView_Previews: PreviewProvider { + static var previews: some View { + PeersView() + } +} diff --git a/YggdrasilSwiftUI/Preview Content/Preview Assets.xcassets/Contents.json b/YggdrasilSwiftUI/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/YggdrasilSwiftUI/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/YggdrasilSwiftUI/SettingsView.swift b/YggdrasilSwiftUI/SettingsView.swift new file mode 100644 index 0000000..61aaf90 --- /dev/null +++ b/YggdrasilSwiftUI/SettingsView.swift @@ -0,0 +1,84 @@ +// +// SettingsView.swift +// YggdrasilSwiftUI +// +// Created by Neil on 10/02/2023. +// + +import SwiftUI + +struct SettingsView: View { + @State private var deviceName = "" + + @State private var autoStartOnWiFi = false + @State private var autoStartOnCellular = false + + var body: some View { + Form { + Section(content: { + TextField("Device Name", text: $deviceName) + }, header: { + Text("Public Identity") + }) + + Section(content: { + Toggle("Wi-Fi", isOn: $autoStartOnWiFi) + Toggle("Mobile Network", isOn: $autoStartOnCellular) + }, header: { + Text("Automatically start when connected to") + }) + + Section(content: { + VStack(alignment: .leading) { + Button("Import configuration") { + + } + #if os(macOS) + .buttonStyle(.link) + #endif + Text("Import configuration from another device, including the public key and Yggdrasil IP address.") + .font(.system(size: 11)) + .foregroundColor(.gray) + } + + VStack(alignment: .leading) { + Button("Export configuration") { + + } + #if os(macOS) + .buttonStyle(.link) + #endif + Text("Configuration will be exported as a file. Your configuration contains your private key which is extremely sensitive. Do not share it with anyone.") + .font(.system(size: 11)) + .foregroundColor(.gray) + } + + VStack(alignment: .leading) { + Button("Reset configuration") { + + } + #if os(macOS) + .buttonStyle(.link) + #endif + .foregroundColor(.red) + Text("Resetting will overwrite with newly generated configuration. Your public key and Yggdrasil IP address will change.") + .font(.system(size: 11)) + .foregroundColor(.gray) + } + }, header: { + Text("Configuration") + }) + } + .formStyle(.grouped) + .navigationTitle("Settings") + #if os(iOS) + .navigationBarTitleDisplayMode(.large) + #endif + } +} + +struct SettingsView_Previews: PreviewProvider { + static var previews: some View { + SettingsView() + } +} diff --git a/YggdrasilSwiftUI/StatusView.swift b/YggdrasilSwiftUI/StatusView.swift new file mode 100644 index 0000000..6cbad4f --- /dev/null +++ b/YggdrasilSwiftUI/StatusView.swift @@ -0,0 +1,125 @@ +// +// ContentView.swift +// YggdrasilSwiftUI +// +// Created by Neil on 10/02/2023. +// + +import SwiftUI + +#if os(iOS) +typealias MyListStyle = DefaultListStyle +#else +typealias MyListStyle = SidebarListStyle +#endif + +struct StatusView: View { + @Binding public var yggdrasilConfiguration: ConfigurationProxy + + @ObservedObject private var appDelegate = Application.appDelegate + + @State private var statusBadgeColor: SwiftUI.Color = .gray + @State private var statusBadgeText: String = "Not enabled" + + private func getStatusBadgeColor() -> SwiftUI.Color { + if !appDelegate.yggdrasilEnabled { + return .gray + } else if !appDelegate.yggdrasilConnected { + return .yellow + } else { + return .green + } + } + + private func getStatusBadgeText() -> String { + if !appDelegate.yggdrasilEnabled { + return "Not enabled" + } else if !appDelegate.yggdrasilConnected { + return "Not connected" + } else { + return "Connected" + } + } + + var body: some View { + Form { + Section(content: { + VStack(alignment: .leading) { + Toggle("Enable Yggdrasil", isOn: $appDelegate.yggdrasilEnabled) + .onTapGesture { + appDelegate.toggleYggdrasil() + } + HStack { + Image(systemName: "circlebadge.fill") + .foregroundColor(statusBadgeColor) + .onAppear(perform: { + statusBadgeColor = getStatusBadgeColor() + }) + .onChange(of: appDelegate.yggdrasilEnabled) { newValue in + statusBadgeColor = getStatusBadgeColor() + } + Text(statusBadgeText) + .foregroundColor(.gray) + .font(.system(size: 11)) + .onAppear(perform: { + statusBadgeText = getStatusBadgeText() + }) + .onChange(of: appDelegate.yggdrasilEnabled) { newValue in + statusBadgeText = getStatusBadgeText() + } + } + } + HStack { + Text("Version") + Spacer() + Text(appDelegate.yggdrasilVersion()) + .foregroundColor(Color.gray) + } + }) + + Section(content: { + HStack { + Text("IP") + Spacer() + Text(appDelegate.yggdrasilIP) + .foregroundColor(Color.gray) + } + HStack { + Text("Subnet") + Spacer() + Text(appDelegate.yggdrasilSubnet) + .foregroundColor(Color.gray) + } + HStack { + Text("Coordinates") + Spacer() + Text(appDelegate.yggdrasilCoords) + .foregroundColor(Color.gray) + } + /* + HStack { + Text("Public Key") + Spacer() + Text("N/A") + .foregroundColor(Color.gray) + .font(.system(size: 15, design: .monospaced)) + .truncationMode(.tail) + } + */ + }) + } + .formStyle(.grouped) + .navigationTitle("Yggdrasil") + #if os(iOS) + .navigationBarTitleDisplayMode(.large) + #endif + } +} + +struct StatusView_Previews: PreviewProvider { + static var previews: some View { + @State var config = ConfigurationProxy() + + StatusView(yggdrasilConfiguration: $config) + } +} diff --git a/YggdrasilSwiftUI/YggdrasilSwiftUI.entitlements b/YggdrasilSwiftUI/YggdrasilSwiftUI.entitlements new file mode 100644 index 0000000..47b72d5 --- /dev/null +++ b/YggdrasilSwiftUI/YggdrasilSwiftUI.entitlements @@ -0,0 +1,22 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-only + + com.apple.developer.networking.networkextension + + packet-tunnel-provider + + com.apple.developer.networking.vpn.api + + allow-vpn + + com.apple.security.application-groups + + group.eu.neilalexander.yggdrasil + + +