From 9ce78d5007aeb490624bb345028f9d8fd33ac475 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 8 Feb 2023 22:56:37 +0000 Subject: [PATCH] Various tweaks --- .../ConfigurationProxy.swift | 252 ++++++++++++++++++ .../CrossPlatformAppDelegate.swift | 158 +++++++++++ .../PacketTunnelProvider.swift | 16 +- .../Application/NSNotification.swift | 2 +- Yggdrasil Network iOS/Extensions/Data.swift | 20 ++ Yggdrasil Network iOS/Info.plist | 5 +- Yggdrasil Network.xcodeproj/project.pbxproj | 31 ++- .../xcschemes/YggdrasilNetwork.xcscheme | 2 +- .../YggdrasilNetworkExtension.xcscheme | 3 +- 9 files changed, 471 insertions(+), 18 deletions(-) create mode 100644 Yggdrasil Network Cross-Platform/ConfigurationProxy.swift create mode 100644 Yggdrasil Network Cross-Platform/CrossPlatformAppDelegate.swift create mode 100644 Yggdrasil Network iOS/Extensions/Data.swift diff --git a/Yggdrasil Network Cross-Platform/ConfigurationProxy.swift b/Yggdrasil Network Cross-Platform/ConfigurationProxy.swift new file mode 100644 index 0000000..de0ad86 --- /dev/null +++ b/Yggdrasil Network Cross-Platform/ConfigurationProxy.swift @@ -0,0 +1,252 @@ +// +// ConfigurationProxy.swift +// YggdrasilNetwork +// +// Created by Neil Alexander on 07/01/2019. +// + +#if canImport(UIKit) +import UIKit +#elseif canImport(AppKit) +import AppKit +#endif +import Yggdrasil +import NetworkExtension + +#if os(iOS) +class PlatformItemSource: NSObject, UIActivityItemSource { + func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any { + return "yggdrasil.conf" + } + + func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? { + return nil + } +} +#elseif os(OSX) +class PlatformItemSource: NSObject {} +#endif + +class ConfigurationProxy: PlatformItemSource { + + private var json: Data? = nil + private var dict: [String: Any]? = nil + + override init() { + super.init() + self.json = MobileGenerateConfigJSON() + do { + try self.convertToDict() + } catch { + NSLog("ConfigurationProxy: Error deserialising JSON (\(error))") + } + #if os(iOS) + self.set("name", inSection: "NodeInfo", to: UIDevice.current.name) + #elseif os(OSX) + self.set("name", inSection: "NodeInfo", to: Host.current().localizedName ?? "") + #endif + self.fix() + } + + init(json: Data) throws { + super.init() + self.json = json + try self.convertToDict() + self.fix() + } + + private func fix() { + self.set("Listen", to: [] as [String]) + self.set("AdminListen", to: "none") + self.set("IfName", to: "dummy") + + if self.get("AutoStart") == nil { + self.set("AutoStart", to: ["WiFi": false, "Mobile": false] as [String: Bool]) + } + + let multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? [] + if multicastInterfaces.count == 0 { + self.set("MulticastInterfaces", to: [ + [ + "Regex": "en.*", + "Beacon": true, + "Listen": true, + ] + ]) + } + } + + public var multicastBeacons: Bool { + get { + let multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? [] + if multicastInterfaces.count == 0 { + return false + } + return multicastInterfaces[0]["Beacon"] as? Bool ?? true + } + set { + var multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? [] + multicastInterfaces[0]["Beacon"] = newValue + self.set("MulticastInterfaces", to: multicastInterfaces) + } + } + + public var multicastListen: Bool { + get { + let multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? [] + if multicastInterfaces.count == 0 { + return false + } + return multicastInterfaces[0]["Listen"] as? Bool ?? true + } + set { + var multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? [] + multicastInterfaces[0]["Listen"] = newValue + self.set("MulticastInterfaces", to: multicastInterfaces) + } + } + + func get(_ key: String) -> Any? { + if let dict = self.dict { + if dict.keys.contains(key) { + return dict[key] + } + } + return nil + } + + func get(_ key: String, inSection section: String) -> Any? { + if let dict = self.get(section) as? [String: Any] { + if dict.keys.contains(key) { + return dict[key] + } + } + return nil + } + + func add(_ value: Any, in key: String) { + if self.dict != nil { + if self.dict![key] as? [Any] != nil { + var temp = self.dict![key] as? [Any] ?? [] + temp.append(value) + self.dict!.updateValue(temp, forKey: key) + } + } + } + + func remove(_ value: String, from key: String) { + if self.dict != nil { + if self.dict![key] as? [String] != nil { + var temp = self.dict![key] as? [String] ?? [] + if let index = temp.firstIndex(of: value) { + temp.remove(at: index) + } + self.dict!.updateValue(temp, forKey: key) + } + } + } + + func remove(index: Int, from key: String) { + if self.dict != nil { + if self.dict![key] as? [Any] != nil { + var temp = self.dict![key] as? [Any] ?? [] + temp.remove(at: index) + self.dict!.updateValue(temp, forKey: key) + } + } + } + + func set(_ key: String, to value: Any) { + if self.dict != nil { + self.dict![key] = value + } + } + + func set(_ key: String, inSection section: String, to value: Any?) { + if self.dict != nil { + if self.dict!.keys.contains(section), let value = value { + var temp = self.dict![section] as? [String: Any] ?? [:] + temp.updateValue(value, forKey: key) + self.dict!.updateValue(temp, forKey: section) + } + } + } + + func data() -> Data? { + do { + try self.convertToJson() + return self.json + } catch { + return nil + } + } + + func save(to manager: inout NETunnelProviderManager) throws { + 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)" + + let disconnectrule = NEOnDemandRuleDisconnect() + var rules: [NEOnDemandRule] = [disconnectrule] + if self.get("WiFi", inSection: "AutoStart") as? Bool ?? false { + let wifirule = NEOnDemandRuleConnect() + wifirule.interfaceTypeMatch = .wiFi + rules.insert(wifirule, at: 0) + } + #if canImport(UIKit) + if self.get("Mobile", inSection: "AutoStart") as? Bool ?? false { + let mobilerule = NEOnDemandRuleConnect() + mobilerule.interfaceTypeMatch = .cellular + rules.insert(mobilerule, at: 0) + } + #endif + manager.onDemandRules = rules + manager.isOnDemandEnabled = rules.count > 1 + providerProtocol.disconnectOnSleep = rules.count > 1 + + manager.protocolConfiguration = providerProtocol + + manager.saveToPreferences(completionHandler: { (error:Error?) in + if let error = error { + print(error) + } else { + print("Save successfully") + NotificationCenter.default.post(name: NSNotification.Name.YggdrasilSettingsUpdated, object: self) + } + }) + } + } + + private func convertToDict() throws { + self.dict = try JSONSerialization.jsonObject(with: self.json!, options: []) as? [String: Any] + } + + private func convertToJson() throws { + self.json = try JSONSerialization.data(withJSONObject: self.dict as Any, options: .prettyPrinted) + } + + #if canImport(UIKit) + override func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any { + return "yggdrasil.conf" + } + + override func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? { + return self.data() + } + + func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivity.ActivityType?) -> String { + if let pubkey = self.get("PublicKey") as? String { + return "yggdrasil-\(pubkey).conf.json" + } + return "yggdrasil.conf.json" + } + #endif +} diff --git a/Yggdrasil Network Cross-Platform/CrossPlatformAppDelegate.swift b/Yggdrasil Network Cross-Platform/CrossPlatformAppDelegate.swift new file mode 100644 index 0000000..1e499d4 --- /dev/null +++ b/Yggdrasil Network Cross-Platform/CrossPlatformAppDelegate.swift @@ -0,0 +1,158 @@ +// +// AppDelegateExtension.swift +// Yggdrasil Network +// +// Created by Neil Alexander on 11/01/2019. +// + +import Foundation +import NetworkExtension +import Yggdrasil +import UIKit + +class CrossPlatformAppDelegate: PlatformAppDelegate { + 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() + } + NotificationCenter.default.addObserver(forName: .NEVPNStatusDidChange, object: nil, queue: nil, using: { notification in + if let conn = notification.object as? NEVPNConnection { + self.updateStatus(conn: conn) + } + }) + self.updateStatus(conn: self.vpnManager.connection) + } + + func updateStatus(conn: NEVPNConnection) { + if conn.status == .connected { + self.makeIPCRequests() + } else if conn.status == .disconnecting || conn.status == .disconnected { + self.clearStatus() + } + } + + func applicationWillResignActive(_ application: UIApplication) { + if self.yggdrasilAdminTimer != nil { + self.yggdrasilAdminTimer!.suspend() + } + } + + func vpnTunnelProviderManagerInit() { + NETunnelProviderManager.loadAllFromPreferences { (savedManagers: [NETunnelProviderManager]?, error: Error?) in + if let error = error { + print(error) + } + + if let savedManagers = savedManagers { + for manager in savedManagers { + if (manager.protocolConfiguration as? NETunnelProviderProtocol)?.providerBundleIdentifier == self.yggdrasilComponent { + print("Found saved VPN Manager") + self.vpnManager = manager + } + } + } + + self.vpnManager.loadFromPreferences(completionHandler: { (error: Error?) in + if let error = error { + print(error) + } + + 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 { + print("Generating new protocol configuration") + self.yggdrasilConfig = ConfigurationProxy() + } + + self.vpnManager.localizedDescription = "Yggdrasil" + self.vpnManager.isEnabled = true + + if let config = self.yggdrasilConfig { + try? config.save(to: &self.vpnManager) + } + }) + } + } + + func makeIPCRequests() { + if self.vpnManager.connection.status != .connected { + return + } + 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)! + 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)! + 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)! + NotificationCenter.default.post(name: .YggdrasilSelfUpdated, object: nil) + } + } + try? session.sendProviderMessage("peers".data(using: .utf8)!) { (peers) in + if let peers = peers { + if let jsonResponse = try? JSONSerialization.jsonObject(with: peers, options: []) as? [[String: Any]] { + self.yggdrasilPeers = jsonResponse + NotificationCenter.default.post(name: .YggdrasilPeersUpdated, object: nil) + } + } + } + try? session.sendProviderMessage("dht".data(using: .utf8)!) { (peers) in + if let peers = peers { + if let jsonResponse = try? JSONSerialization.jsonObject(with: peers, options: []) as? [[String: Any]] { + self.yggdrasilDHT = jsonResponse + NotificationCenter.default.post(name: .YggdrasilDHTUpdated, object: nil) + } + } + } + } + } + + func clearStatus() { + self.yggdrasilSelfIP = "N/A" + self.yggdrasilSelfSubnet = "N/A" + self.yggdrasilSelfCoords = "[]" + self.yggdrasilPeers = [] + self.yggdrasilDHT = [] + NotificationCenter.default.post(name: .YggdrasilSelfUpdated, object: nil) + NotificationCenter.default.post(name: .YggdrasilPeersUpdated, object: nil) + NotificationCenter.default.post(name: .YggdrasilDHTUpdated, object: nil) + } +} diff --git a/Yggdrasil Network Extension/PacketTunnelProvider.swift b/Yggdrasil Network Extension/PacketTunnelProvider.swift index a444de0..dfdb32c 100644 --- a/Yggdrasil Network Extension/PacketTunnelProvider.swift +++ b/Yggdrasil Network Extension/PacketTunnelProvider.swift @@ -9,24 +9,28 @@ class PacketTunnelProvider: NEPacketTunnelProvider { private var readThread: Thread? private var writeThread: Thread? - private var writeBuffer = Data(count: 65535) + private let readBuffer = NSMutableData(length: 65535) + private let writeBuffer = Data(count: 65535) @objc func readPacketsFromTun() { - autoreleasepool { - self.packetFlow.readPackets { (packets: [Data], protocols: [NSNumber]) in + self.packetFlow.readPackets { (packets: [Data], protocols: [NSNumber]) in + autoreleasepool { for packet in packets { try? self.yggdrasil.sendBuffer(packet, length: packet.count) } - self.readPacketsFromTun() } + self.readPacketsFromTun() } } @objc func writePacketsToTun() { + var n: Int = 0 + let readData = Data(bytesNoCopy: readBuffer!.mutableBytes, count: 65535, deallocator: .none) while true { autoreleasepool { - if let data = try? self.yggdrasil.recv() { - self.packetFlow.writePackets([data], withProtocols: [NSNumber](repeating: AF_INET6 as NSNumber, count: 1)) + try? self.yggdrasil.recvBuffer(readBuffer as Data?, ret0_: &n) + if n > 0 { + self.packetFlow.writePackets([readData[.. UISupportedInterfaceOrientations - UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UISupportedInterfaceOrientations~ipad - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortraitUpsideDown UTExportedTypeDeclarations diff --git a/Yggdrasil Network.xcodeproj/project.pbxproj b/Yggdrasil Network.xcodeproj/project.pbxproj index 5062c55..6c46842 100644 --- a/Yggdrasil Network.xcodeproj/project.pbxproj +++ b/Yggdrasil Network.xcodeproj/project.pbxproj @@ -12,6 +12,9 @@ 3939196D21E39313009320F3 /* UIDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3939196C21E39313009320F3 /* UIDevice.swift */; }; 3939197321E39815009320F3 /* ToggleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3939197221E39815009320F3 /* ToggleTableViewCell.swift */; }; 394A1EB321DEA46400D9F553 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 394A1EB221DEA46400D9F553 /* SettingsViewController.swift */; }; + 3952ADB729945AF700B3835D /* ConfigurationProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3952ADB629945AF700B3835D /* ConfigurationProxy.swift */; }; + 3952ADB829945AF700B3835D /* ConfigurationProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3952ADB629945AF700B3835D /* ConfigurationProxy.swift */; }; + 3952ADBA29945AFA00B3835D /* CrossPlatformAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3952ADB929945AFA00B3835D /* CrossPlatformAppDelegate.swift */; }; 39682A392225AD15004FB670 /* CopyableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39682A382225AD15004FB670 /* CopyableLabel.swift */; }; 3996AF38270328080070947D /* Yggdrasil.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3996AF37270328080070947D /* Yggdrasil.xcframework */; }; 3996AF39270328080070947D /* Yggdrasil.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3996AF37270328080070947D /* Yggdrasil.xcframework */; }; @@ -24,7 +27,7 @@ E593CE761DF8FC3C00D7265D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E593CE751DF8FC3C00D7265D /* Assets.xcassets */; }; E593CE791DF8FC3C00D7265D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E593CE771DF8FC3C00D7265D /* LaunchScreen.storyboard */; }; E593CE9C1DF905AF00D7265D /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E593CE9B1DF905AF00D7265D /* PacketTunnelProvider.swift */; }; - E593CEA01DF905AF00D7265D /* YggdrasilNetworkExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = E593CE971DF905AF00D7265D /* YggdrasilNetworkExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + E593CEA01DF905AF00D7265D /* YggdrasilNetworkExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = E593CE971DF905AF00D7265D /* YggdrasilNetworkExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -38,15 +41,15 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - E593CEA41DF905B000D7265D /* Embed App Extensions */ = { + E593CEA41DF905B000D7265D /* Embed Foundation Extensions */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 13; files = ( - E593CEA01DF905AF00D7265D /* YggdrasilNetworkExtension.appex in Embed App Extensions */, + E593CEA01DF905AF00D7265D /* YggdrasilNetworkExtension.appex in Embed Foundation Extensions */, ); - name = "Embed App Extensions"; + name = "Embed Foundation Extensions"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ @@ -59,6 +62,8 @@ 3939196C21E39313009320F3 /* UIDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDevice.swift; sourceTree = ""; }; 3939197221E39815009320F3 /* ToggleTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleTableViewCell.swift; sourceTree = ""; }; 394A1EB221DEA46400D9F553 /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; + 3952ADB629945AF700B3835D /* ConfigurationProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationProxy.swift; sourceTree = ""; }; + 3952ADB929945AFA00B3835D /* CrossPlatformAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrossPlatformAppDelegate.swift; sourceTree = ""; }; 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; }; @@ -142,6 +147,16 @@ path = "UI Components"; sourceTree = ""; }; + 3952ADB529945AE900B3835D /* Yggdrasil Network Core */ = { + isa = PBXGroup; + children = ( + 3952ADB929945AFA00B3835D /* CrossPlatformAppDelegate.swift */, + 3952ADB629945AF700B3835D /* ConfigurationProxy.swift */, + ); + name = "Yggdrasil Network Core"; + path = "Yggdrasil Network Cross-Platform"; + sourceTree = ""; + }; 399D032221DA775D0016354F /* Frameworks */ = { isa = PBXGroup; children = ( @@ -154,6 +169,7 @@ E593CE621DF8FC3C00D7265D = { isa = PBXGroup; children = ( + 3952ADB529945AE900B3835D /* Yggdrasil Network Core */, 3913E99E21DB9B41001E0EC7 /* YggdrasilNetwork.entitlements */, 3913E99C21DB9B1C001E0EC7 /* YggdrasilNetworkExtension.entitlements */, E593CE981DF905AF00D7265D /* Yggdrasil Network Extension */, @@ -205,7 +221,7 @@ E593CE671DF8FC3C00D7265D /* Sources */, E593CE681DF8FC3C00D7265D /* Frameworks */, E593CE691DF8FC3C00D7265D /* Resources */, - E593CEA41DF905B000D7265D /* Embed App Extensions */, + E593CEA41DF905B000D7265D /* Embed Foundation Extensions */, ); buildRules = ( ); @@ -241,7 +257,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1010; - LastUpgradeCheck = 1250; + LastUpgradeCheck = 1420; ORGANIZATIONNAME = ""; TargetAttributes = { E593CE6A1DF8FC3C00D7265D = { @@ -328,6 +344,8 @@ 3939196D21E39313009320F3 /* UIDevice.swift in Sources */, 394A1EB321DEA46400D9F553 /* SettingsViewController.swift in Sources */, E593CE711DF8FC3C00D7265D /* TableViewController.swift in Sources */, + 3952ADBA29945AFA00B3835D /* CrossPlatformAppDelegate.swift in Sources */, + 3952ADB729945AF700B3835D /* ConfigurationProxy.swift in Sources */, 39682A392225AD15004FB670 /* CopyableLabel.swift in Sources */, E593CE6F1DF8FC3C00D7265D /* AppDelegate.swift in Sources */, 3913E9C021DD3A51001E0EC7 /* SplitViewController.swift in Sources */, @@ -341,6 +359,7 @@ files = ( 39CC924D221DEDD3004960DC /* NSNotification.swift in Sources */, E593CE9C1DF905AF00D7265D /* PacketTunnelProvider.swift in Sources */, + 3952ADB829945AF700B3835D /* ConfigurationProxy.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Yggdrasil Network.xcodeproj/xcshareddata/xcschemes/YggdrasilNetwork.xcscheme b/Yggdrasil Network.xcodeproj/xcshareddata/xcschemes/YggdrasilNetwork.xcscheme index 433fa74..d512f1b 100644 --- a/Yggdrasil Network.xcodeproj/xcshareddata/xcschemes/YggdrasilNetwork.xcscheme +++ b/Yggdrasil Network.xcodeproj/xcshareddata/xcschemes/YggdrasilNetwork.xcscheme @@ -1,6 +1,6 @@