Some SwiftUI

This commit is contained in:
Neil Alexander 2023-06-06 22:09:27 +01:00
parent b58f8e3852
commit 291b12b785
No known key found for this signature in database
GPG key ID: A02A2019A2BB0944
24 changed files with 901 additions and 89 deletions

View file

@ -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)"

View file

@ -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)

View file

@ -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?

View file

@ -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()

View file

@ -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 = "<group>"; };
3996AF37270328080070947D /* Yggdrasil.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Yggdrasil.xcframework; sourceTree = "<group>"; };
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 = "<group>"; };
39BF9FC12A2E9E51000E7269 /* YggdrasilNetworkExtension-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "YggdrasilNetworkExtension-Bridging-Header.h"; sourceTree = "<group>"; };
39BF9FC22A2E9E52000E7269 /* PacketTunnelProvider+FileDescriptor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PacketTunnelProvider+FileDescriptor.h"; sourceTree = "<group>"; };
39BF9FC52A2F5FA7000E7269 /* PacketTunnelProvider+FileDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PacketTunnelProvider+FileDescriptor.swift"; sourceTree = "<group>"; };
39CC924B221DEDCE004960DC /* NSNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSNotification.swift; sourceTree = "<group>"; };
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 = "<group>"; };
39F0205F2996CD760093F603 /* StatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusView.swift; sourceTree = "<group>"; };
39F020612996CD770093F603 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
39F020632996CD770093F603 /* YggdrasilSwiftUI.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = YggdrasilSwiftUI.entitlements; sourceTree = "<group>"; };
39F020652996CD770093F603 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
39F0206A2996CF260093F603 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
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 = "<group>"; };
E593CE701DF8FC3C00D7265D /* TableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = "<group>"; };
@ -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 = "<group>";
@ -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 = "<group>";
};
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 = "<group>";
};
39F020642996CD770093F603 /* Preview Content */ = {
isa = PBXGroup;
children = (
39F020652996CD770093F603 /* Preview Assets.xcassets */,
);
path = "Preview Content";
sourceTree = "<group>";
};
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 = "<group>";
@ -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 = "<group>";
@ -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 = (

View file

@ -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
}
}

View file

@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

View file

@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View file

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -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()
}
}

View file

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -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()
}
}

View file

@ -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)
}
}

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>packet-tunnel-provider</string>
</array>
<key>com.apple.developer.networking.vpn.api</key>
<array>
<string>allow-vpn</string>
</array>
<key>com.apple.security.application-groups</key>
<array>
<string>group.eu.neilalexander.yggdrasil</string>
</array>
</dict>
</plist>