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 0000000..eee5a75 Binary files /dev/null and b/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy-1.png differ diff --git a/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy-2.png b/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy-2.png new file mode 100644 index 0000000..567267c Binary files /dev/null and b/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy-2.png differ diff --git a/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy-3.png b/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy-3.png new file mode 100644 index 0000000..42ab80b Binary files /dev/null and b/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy-3.png differ 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 0000000..cc8b151 Binary files /dev/null and b/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy-4.png differ 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 0000000..d658df2 Binary files /dev/null and b/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy-5.png differ diff --git a/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy.png b/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy.png new file mode 100644 index 0000000..720ec2e Binary files /dev/null and b/YggdrasilSwiftUI/Assets.xcassets/AppIcon 1.appiconset/drawing copy.png differ diff --git a/YggdrasilSwiftUI/Assets.xcassets/AppIcon.appiconset/Contents.json b/YggdrasilSwiftUI/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..4df0353 --- /dev/null +++ b/YggdrasilSwiftUI/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,65 @@ +{ + "images" : [ + { + "filename" : "drawing copy-4 1.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "filename" : "drawing copy-4.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/YggdrasilSwiftUI/Assets.xcassets/AppIcon.appiconset/drawing copy-4 1.png b/YggdrasilSwiftUI/Assets.xcassets/AppIcon.appiconset/drawing copy-4 1.png new file mode 100644 index 0000000..cc8b151 Binary files /dev/null and b/YggdrasilSwiftUI/Assets.xcassets/AppIcon.appiconset/drawing copy-4 1.png differ 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 0000000..cc8b151 Binary files /dev/null and b/YggdrasilSwiftUI/Assets.xcassets/AppIcon.appiconset/drawing copy-4.png differ 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 + + +