Introduce initial UI for connecting to networks

This commit is contained in:
Conrad Kramer 2024-02-24 09:49:07 -08:00
parent a757ac7be9
commit 453dd2d116
33 changed files with 1458 additions and 321 deletions

View file

@ -46,7 +46,6 @@ opt_in_rules:
- multiline_parameters - multiline_parameters
- multiline_parameters_brackets - multiline_parameters_brackets
- no_extension_access_modifier - no_extension_access_modifier
- no_grouping_extension
- nslocalizedstring_key - nslocalizedstring_key
- nslocalizedstring_require_bundle - nslocalizedstring_require_bundle
- number_separator - number_separator
@ -76,9 +75,7 @@ opt_in_rules:
- sorted_first_last - sorted_first_last
- sorted_imports - sorted_imports
- static_operator - static_operator
- strict_fileprivate
- strong_iboutlet - strong_iboutlet
- switch_case_on_newline
- test_case_accessibility - test_case_accessibility
- toggle_bool - toggle_bool
- trailing_closure - trailing_closure
@ -97,3 +94,5 @@ disabled_rules:
- force_try - force_try
- nesting - nesting
- todo - todo
- trailing_comma
- switch_case_on_newline

View file

@ -11,7 +11,12 @@ INFOPLIST_KEY_UIStatusBarStyle[sdk=iphone*] = UIStatusBarStyleDefault
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad[sdk=iphone*] = UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad[sdk=iphone*] = UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone[sdk=iphone*] = UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone[sdk=iphone*] = UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
TARGETED_DEVICE_FAMILY[sdk=iphone*] = 1,2 TARGETED_DEVICE_FAMILY[sdk=iphone*] = 1,2
EXCLUDED_SOURCE_FILE_NAMES = MainMenu.xib
EXCLUDED_SOURCE_FILE_NAMES[sdk=macosx*] =
INFOPLIST_KEY_LSUIElement[sdk=macosx*] = YES
INFOPLIST_KEY_NSMainNibFile[sdk=macosx*] = MainMenu
INFOPLIST_KEY_NSPrincipalClass[sdk=macosx*] = NSApplication
INFOPLIST_KEY_LSApplicationCategoryType[sdk=macosx*] = public.app-category.utilities INFOPLIST_KEY_LSApplicationCategoryType[sdk=macosx*] = public.app-category.utilities
CODE_SIGN_ENTITLEMENTS = App/App-iOS.entitlements CODE_SIGN_ENTITLEMENTS = App/App-iOS.entitlements

View file

@ -3,6 +3,7 @@ import AppKit
import SwiftUI import SwiftUI
@MainActor @MainActor
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate { class AppDelegate: NSObject, NSApplicationDelegate {
private let quitItem: NSMenuItem = { private let quitItem: NSMenuItem = {
let quitItem = NSMenuItem( let quitItem = NSMenuItem(
@ -16,7 +17,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}() }()
private let toggleItem: NSMenuItem = { private let toggleItem: NSMenuItem = {
let toggleView = NSHostingView(rootView: MenuItemToggleView(tunnel: BurrowApp.tunnel)) let toggleView = NSHostingView(rootView: MenuItemToggleView())
toggleView.frame.size = CGSize(width: 300, height: 32) toggleView.frame.size = CGSize(width: 300, height: 32)
toggleView.autoresizingMask = [.width] toggleView.autoresizingMask = [.width]

View file

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x50",
"green" : "0x37",
"red" : "0xEC"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "flag-standalone-wtransparent.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x1A",
"green" : "0x17",
"red" : "0x88"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "WireGuard.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 46 79" xmlns="http://www.w3.org/2000/svg">
<g stroke-width=".265" transform="matrix(1, 0, 0, 1, 144.316635, -78.301682)">
<path d="M -131.805 103.359 C -123.863 98.5 -113.717 101.469 -109.915 108.776 C -109.195 110.161 -109.103 112.293 -109.559 113.746 C -111.135 118.761 -114.855 121.574 -119.961 122.769 C -118.455 121.48 -117.257 120.019 -116.876 117.999 C -116.474 116.064 -116.911 114.05 -118.078 112.455 C -119.937 109.901 -123.263 108.888 -126.23 109.971 C -129.373 111.164 -131.095 114.033 -130.785 117.56 C -130.497 120.836 -128.011 122.959 -123.36 123.765 C -124.056 124.133 -124.591 124.404 -125.115 124.696 C -127.245 125.863 -129.099 127.475 -130.55 129.423 C -131.022 130.06 -131.347 130.112 -132.066 129.672 C -141.415 123.955 -142.016 109.605 -131.806 103.359 L -131.805 103.359 Z M -138.803 138.688 C -140.305 139.07 -141.761 139.634 -143.296 140.138 C -142.545 135.071 -136.612 130.404 -131.594 130.936 C -133.048 132.939 -133.896 135.317 -134.039 137.787 C -135.707 138.094 -137.278 138.301 -138.803 138.688 Z M -106.844 89.217 C -105.36 89.271 -103.873 89.248 -102.388 89.284 C -102.017 89.308 -101.649 89.359 -101.285 89.437 C -101.617 89.947 -101.992 90.428 -102.406 90.875 C -102.937 91.37 -103.537 91.853 -104.302 91.101 C -104.486 90.92 -104.921 90.962 -105.241 90.958 C -106.718 90.938 -108.197 90.891 -109.672 90.947 C -110.951 90.988 -112.227 91.118 -113.488 91.336 C -113.725 91.379 -114.078 92.165 -113.969 92.455 C -113.713 93.139 -113.339 93.893 -112.785 94.331 C -110.737 95.947 -108.559 97.399 -106.501 99.004 C -104.502 100.564 -102.641 102.274 -101.507 104.628 C -100.03 107.694 -99.987 110.91 -100.624 114.139 C -101.688 119.531 -104.416 123.998 -108.835 127.242 C -110.615 128.55 -112.819 129.292 -114.858 130.231 C -116.652 131.057 -118.498 131.769 -120.295 132.586 C -123.536 134.06 -125.357 137.577 -124.822 141.235 C -124.33 144.591 -121.386 147.392 -118.013 147.97 C -113.967 148.664 -109.792 146.034 -108.802 141.922 C -107.689 137.297 -110.202 133.168 -114.905 131.917 C -115.112 131.862 -115.32 131.81 -115.752 131.698 C -114.494 131.136 -113.407 130.735 -112.404 130.183 C -110.654 129.22 -108.936 128.201 -107.249 127.124 C -106.754 126.807 -106.486 126.807 -106.063 127.173 C -102.828 129.969 -100.899 133.448 -100.358 137.713 C -99.462 144.773 -102.804 151.259 -109.108 154.584 C -118.86 159.727 -130.794 153.873 -132.948 143.061 C -134.794 133.799 -128.257 125.399 -120.391 123.777 C -117.008 123.079 -113.914 121.671 -111.509 119.065 C -109.957 117.384 -109.205 115.942 -108.948 115.291 C -108.471 114.071 -108.227 112.772 -108.228 111.462 C -108.28 110.329 -108.546 109.216 -109.013 108.182 C -109.834 106.31 -112.98 103.332 -113.759 102.704 L -121.168 96.904 C -121.429 96.689 -121.723 96.705 -122.36 96.748 C -123.117 96.799 -125.053 96.906 -125.888 96.688 C -125.212 96.176 -123.371 95.432 -122.58 94.834 C -124.98 93.212 -127.721 93.798 -130.237 93.313 C -129.655 92.23 -126.776 90.564 -125.139 90.379 C -125.236 89.464 -125.385 88.556 -125.585 87.659 C -125.685 87.291 -126.096 86.934 -126.455 86.723 C -127.324 86.214 -128.246 85.793 -129.246 85.286 C -128.35 84.707 -127.313 84.386 -126.247 84.358 C -125.238 84.32 -124.228 84.418 -123.245 84.651 C -121.461 85.058 -120.037 84.792 -118.618 83.58 C -119.735 83.13 -120.852 82.719 -121.935 82.233 C -123.003 81.746 -124.043 81.202 -125.052 80.604 C -122.241 80.994 -119.523 82.048 -116.649 81.663 C -116.625 81.532 -116.6 81.402 -116.576 81.271 C -118.724 80.771 -120.873 80.271 -123.251 79.717 C -119.272 79.353 -115.567 79.293 -112.059 81.002 C -111.072 81.482 -110.039 81.88 -109.093 82.43 C -108.631 82.697 -108.321 83.225 -107.942 83.636 C -107.641 83.962 -107.4 84.399 -107.03 84.595 C -105.628 85.341 -104.084 85.37 -102.512 85.333 C -102.5 85.154 -102.489 84.986 -102.477 84.806 C -100.894 85.3 -99.113 87.125 -99.116 88.458 C -101.68 88.458 -104.242 88.449 -106.804 88.473 C -107.077 88.475 -107.349 88.675 -107.622 88.784 C -107.363 88.935 -107.108 89.207 -106.844 89.217 Z" style="fill: rgb(255, 255, 255);"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4 KiB

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "WireGuardTitle.svg",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,3 @@
<svg width="1538" height="210" viewBox="0 0 1538 210" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M65.7132 204.476L0.500244 4.49222H24.9555L74.5901 158.827L125.493 4.49222H147.774L198.496 158.467L248.311 4.49222H272.041L207.009 204.476H188.895L136.182 43.4409L84.0113 204.476H65.7132ZM267.577 204.476V49.4109H292.213V204.476H267.577ZM349.324 136.908V204.476H325.051V50.1407H431.568C447.387 50.1407 459.766 53.9449 468.703 61.5533C477.638 69.1618 482.107 79.7288 482.107 93.2544C482.288 103.759 478.504 113.946 471.51 121.785C464.445 129.818 455.237 134.739 443.885 136.548L486.817 204.478H460.551L416.713 136.91L349.324 136.908ZM349.324 114.265H431.565C439.898 114.265 446.268 112.453 450.676 108.83C455.083 105.207 457.287 100.014 457.288 93.2518C457.288 86.49 455.084 81.3275 450.676 77.7643C446.266 74.2033 439.896 72.422 431.565 72.4202H349.324V114.265ZM504.99 204.476V49.7808H650.085V72.4241H529.259V111.008H608.602V133.289H529.259V182.198H656.785V204.48H504.98L504.99 204.476ZM827.46 150.318V120.972H772.934V97.9663H852.277V158.107C842.614 174.773 830.025 187.543 814.508 196.419C798.989 205.296 781.387 209.734 761.704 209.734C731.752 209.734 707.026 199.861 687.524 180.116C668.02 160.371 658.269 135.343 658.269 105.031C658.269 74.5986 668.051 49.5097 687.615 29.7643C707.179 10.0196 731.876 0.147217 761.704 0.147217C780.181 0.147217 797.028 4.19276 812.244 12.2839C827.443 20.3602 840.25 32.2924 849.379 46.8828L828.547 61.7363C822.413 49.9333 812.97 40.1755 801.375 33.6582C789.281 26.8286 775.592 23.3281 761.704 23.5135C739.242 23.5135 720.644 31.2122 705.911 46.6097C691.175 62.0066 683.808 81.4796 683.81 105.029C683.81 128.578 691.177 148.021 705.911 163.358C720.643 178.696 739.241 186.365 761.704 186.363C775.711 186.363 788.18 183.344 799.111 177.306C810.039 171.269 819.488 162.274 827.46 150.318ZM879.358 50.1436H903.631V149.231C903.631 164.085 907.134 174.108 914.138 179.301C921.141 184.494 935.029 187.091 955.802 187.09C976.693 187.09 990.642 184.494 997.647 179.301C1004.65 174.11 1008.15 164.087 1008.15 149.231V50.1436H1032.25V155.57C1032.25 174.531 1026.24 188.238 1014.22 196.691C1002.2 205.142 982.61 209.369 955.439 209.372C928.386 209.372 908.943 205.205 897.11 196.872C885.273 188.539 879.355 174.772 879.357 155.57L879.358 50.1436ZM1028.45 204.476L1107.07 49.7808H1122.29L1201.63 204.476H1175.73L1155.62 165.167H1073.92L1053.99 204.476H1028.45ZM1084.43 144.698H1144.93L1114.86 85.4626L1084.43 144.698ZM1228.55 136.908V204.476H1204.27V50.1406H1310.79C1326.61 50.1406 1338.99 53.9448 1347.93 61.5532C1356.86 69.1617 1361.33 79.7287 1361.33 93.2543C1361.51 103.759 1357.73 113.945 1350.73 121.784C1343.67 129.817 1334.46 134.739 1323.11 136.548L1366.04 204.478H1339.77L1295.94 136.91L1228.55 136.908ZM1228.55 114.265H1310.79C1319.12 114.265 1325.49 112.453 1329.9 108.83C1334.31 105.207 1336.51 100.014 1336.51 93.2517C1336.51 86.4899 1334.31 81.3274 1329.9 77.7642C1325.49 74.2032 1319.12 72.4218 1310.79 72.4201H1228.55V114.265ZM1453.24 49.7818C1478.48 49.7818 1498.83 56.9973 1514.29 71.4281C1529.74 85.861 1537.47 104.61 1537.47 127.674C1537.47 150.983 1529.9 169.611 1514.74 183.559C1499.58 197.506 1479.08 204.48 1453.24 204.481H1384.59V49.786H1453.24L1453.24 49.7818ZM1453.6 72.0631H1408.86V182.2H1453.6C1471.96 182.2 1486.39 177.278 1496.9 167.436C1507.4 157.595 1512.66 144.221 1512.66 127.312C1512.66 111.009 1507.22 97.7246 1496.35 87.4596C1485.48 77.1966 1471.23 72.0651 1453.6 72.0631Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View file

@ -1,21 +1,13 @@
import SwiftUI import SwiftUI
@main #if !os(macOS)
@MainActor @MainActor
@main
struct BurrowApp: App { struct BurrowApp: App {
static let tunnel = Tunnel { manager, proto in
proto.serverAddress = "hackclub.com"
manager.localizedDescription = "Burrow"
}
#if os(macOS)
@NSApplicationDelegateAdaptor(AppDelegate.self)
var delegate
#endif
var body: some Scene { var body: some Scene {
WindowGroup { WindowGroup {
TunnelView(tunnel: Self.tunnel) BurrowView()
} }
} }
} }
#endif

View file

@ -0,0 +1,26 @@
import SwiftUI
struct BurrowView: View {
var body: some View {
NavigationStack {
VStack {
NetworkCarouselView()
Spacer()
TunnelStatusView()
TunnelButton()
.padding(.bottom)
}
.padding()
.navigationTitle("Networks")
}
}
}
#if DEBUG
struct NetworkView_Previews: PreviewProvider {
static var previews: some View {
BurrowView()
.environment(\.tunnel, PreviewTunnel())
}
}
#endif

View file

@ -0,0 +1,50 @@
import SwiftUI
struct FloatingButtonStyle: ButtonStyle {
static let duration = 0.08
var color: Color
var cornerRadius: CGFloat
func makeBody(configuration: Configuration) -> some View {
configuration.label
.font(.headline)
.foregroundColor(.white)
.frame(minHeight: 48)
.padding(.horizontal)
.background(
RoundedRectangle(cornerRadius: cornerRadius)
.fill(
LinearGradient(
colors: [
configuration.isPressed ? color.opacity(0.9) : color.opacity(0.9),
configuration.isPressed ? color.opacity(0.9) : color
],
startPoint: .init(x: 0.2, y: 0),
endPoint: .init(x: 0.8, y: 1)
)
)
.background(
RoundedRectangle(cornerRadius: cornerRadius)
.fill(configuration.isPressed ? .black : .white)
)
)
.shadow(color: .black.opacity(configuration.isPressed ? 0.0 : 0.1), radius: 2.5, x: 0, y: 2)
.scaleEffect(configuration.isPressed ? 0.975 : 1.0)
.padding(.bottom, 2)
.animation(
configuration.isPressed ? .easeOut(duration: Self.duration) : .easeIn(duration: Self.duration),
value: configuration.isPressed
)
}
}
extension ButtonStyle where Self == FloatingButtonStyle {
static var floating: FloatingButtonStyle {
floating()
}
static func floating(color: Color = .accentColor, cornerRadius: CGFloat = 10) -> FloatingButtonStyle {
FloatingButtonStyle(color: color, cornerRadius: cornerRadius)
}
}

679
Apple/App/MainMenu.xib Normal file
View file

@ -0,0 +1,679 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22689"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="BurrowApp" customModuleProvider="target"/>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="Burrow" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Burrow" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About Burrow" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Services" id="NMo-om-nkz">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
<menuItem title="Hide Burrow" keyEquivalent="h" id="Olw-nP-bQN">
<connections>
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
</connections>
</menuItem>
<menuItem title="Show All" id="Kd2-mp-pUS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit Burrow" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="File" id="dMs-cI-mzQ">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="File" id="bib-Uj-vzu">
<items>
<menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
<connections>
<action selector="newDocument:" target="-1" id="4Si-XN-c54"/>
</connections>
</menuItem>
<menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
<connections>
<action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
</connections>
</menuItem>
<menuItem title="Open Recent" id="tXI-mr-wws">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
<items>
<menuItem title="Clear Menu" id="vNY-rz-j42">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="clearRecentDocuments:" target="-1" id="Daa-9d-B3U"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
<connections>
<action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
</connections>
</menuItem>
<menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
<connections>
<action selector="saveDocument:" target="-1" id="teZ-XB-qJY"/>
</connections>
</menuItem>
<menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
<connections>
<action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
</connections>
</menuItem>
<menuItem title="Revert to Saved" keyEquivalent="r" id="KaW-ft-85H">
<connections>
<action selector="revertDocumentToSaved:" target="-1" id="iJ3-Pv-kwq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
<menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="runPageLayout:" target="-1" id="Din-rz-gC5"/>
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
<connections>
<action selector="print:" target="-1" id="qaZ-4w-aoO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="5QF-Oa-p0T">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
<items>
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
<connections>
<action selector="undo:" target="-1" id="M6e-cu-g7V"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
<connections>
<action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
<connections>
<action selector="cut:" target="-1" id="YJe-68-I9s"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
<connections>
<action selector="copy:" target="-1" id="G1f-GL-Joy"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
<connections>
<action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
</connections>
</menuItem>
<menuItem title="Delete" id="pa3-QI-u2k">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
<connections>
<action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
<menuItem title="Find" id="4EN-yA-p0u">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Find" id="1b7-l0-nxx">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
<connections>
<action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
</connections>
</menuItem>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
<connections>
<action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
<connections>
<action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
<connections>
<action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
<connections>
<action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
<connections>
<action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="9ic-FL-obx">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
<items>
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
</connections>
</menuItem>
<menuItem title="Smart Links" id="cwL-P1-jid">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
</connections>
</menuItem>
<menuItem title="Data Detectors" id="tRr-pd-1PS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" id="2oI-Rn-ZJC">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
<items>
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="xrE-MZ-jX0">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
<items>
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Format" id="jxT-CU-nIS">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Format" id="GEO-Iw-cKr">
<items>
<menuItem title="Font" id="Gi5-1S-RQB">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
<items>
<menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
<connections>
<action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
</connections>
</menuItem>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
</connections>
</menuItem>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
</connections>
</menuItem>
<menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
<connections>
<action selector="underline:" target="-1" id="FYS-2b-JAY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
</connections>
</menuItem>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
<menuItem title="Kern" id="jBQ-r6-VK2">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Kern" id="tlD-Oa-oAM">
<items>
<menuItem title="Use Default" id="GUa-eO-cwY">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardKerning:" target="-1" id="6dk-9l-Ckg"/>
</connections>
</menuItem>
<menuItem title="Use None" id="cDB-IK-hbR">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffKerning:" target="-1" id="U8a-gz-Maa"/>
</connections>
</menuItem>
<menuItem title="Tighten" id="46P-cB-AYj">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="tightenKerning:" target="-1" id="hr7-Nz-8ro"/>
</connections>
</menuItem>
<menuItem title="Loosen" id="ogc-rX-tC1">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="loosenKerning:" target="-1" id="8i4-f9-FKE"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Ligatures" id="o6e-r0-MWq">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
<items>
<menuItem title="Use Default" id="agt-UL-0e3">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardLigatures:" target="-1" id="7uR-wd-Dx6"/>
</connections>
</menuItem>
<menuItem title="Use None" id="J7y-lM-qPV">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffLigatures:" target="-1" id="iX2-gA-Ilz"/>
</connections>
</menuItem>
<menuItem title="Use All" id="xQD-1f-W4t">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useAllLigatures:" target="-1" id="KcB-kA-TuK"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Baseline" id="OaQ-X3-Vso">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Baseline" id="ijk-EB-dga">
<items>
<menuItem title="Use Default" id="3Om-Ey-2VK">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unscript:" target="-1" id="0vZ-95-Ywn"/>
</connections>
</menuItem>
<menuItem title="Superscript" id="Rqc-34-cIF">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="superscript:" target="-1" id="3qV-fo-wpU"/>
</connections>
</menuItem>
<menuItem title="Subscript" id="I0S-gh-46l">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="subscript:" target="-1" id="Q6W-4W-IGz"/>
</connections>
</menuItem>
<menuItem title="Raise" id="2h7-ER-AoG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="raiseBaseline:" target="-1" id="4sk-31-7Q9"/>
</connections>
</menuItem>
<menuItem title="Lower" id="1tx-W0-xDw">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowerBaseline:" target="-1" id="OF1-bc-KW4"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
<menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
<connections>
<action selector="orderFrontColorPanel:" target="-1" id="mSX-Xz-DV3"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
<menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="copyFont:" target="-1" id="GJO-xA-L4q"/>
</connections>
</menuItem>
<menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteFont:" target="-1" id="JfD-CL-leO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Text" id="Fal-I4-PZk">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Text" id="d9c-me-L2H">
<items>
<menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
<connections>
<action selector="alignLeft:" target="-1" id="zUv-R1-uAa"/>
</connections>
</menuItem>
<menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
<connections>
<action selector="alignCenter:" target="-1" id="spX-mk-kcS"/>
</connections>
</menuItem>
<menuItem title="Justify" id="J5U-5w-g23">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="alignJustified:" target="-1" id="ljL-7U-jND"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
<connections>
<action selector="alignRight:" target="-1" id="r48-bG-YeY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
<menuItem title="Writing Direction" id="H1b-Si-o9J">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
<items>
<menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="YGs-j5-SAR">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionNatural:" target="-1" id="qtV-5e-UBP"/>
</connections>
</menuItem>
<menuItem id="Lbh-J2-qVU">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="S0X-9S-QSf"/>
</connections>
</menuItem>
<menuItem id="jFq-tB-4Kx">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="5fk-qB-AqJ"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
<menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="Nop-cj-93Q">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionNatural:" target="-1" id="lPI-Se-ZHp"/>
</connections>
</menuItem>
<menuItem id="BgM-ve-c93">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="caW-Bv-w94"/>
</connections>
</menuItem>
<menuItem id="RB4-Sm-HuC">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="EXD-6r-ZUu"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
<menuItem title="Show Ruler" id="vLm-3I-IUL">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleRuler:" target="-1" id="FOx-HJ-KwY"/>
</connections>
</menuItem>
<menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="copyRuler:" target="-1" id="71i-fW-3W2"/>
</connections>
</menuItem>
<menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="pasteRuler:" target="-1" id="cSh-wd-qM2"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="HyV-fh-RgO">
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="toggleToolbarShown:" target="-1" id="BXY-wc-z0C"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="runToolbarCustomizationPalette:" target="-1" id="pQI-g3-MTW"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="hB3-LF-h0Y"/>
<menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="toggleSidebar:" target="-1" id="iwa-gc-5KM"/>
</connections>
</menuItem>
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="aUF-d1-5bR">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
<connections>
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="R4o-n2-Eq4">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="wpr-3q-Mcd">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items>
<menuItem title="Burrow Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections>
<action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
<point key="canvasLocation" x="200" y="121"/>
</menu>
</objects>
</document>

View file

@ -1,60 +0,0 @@
//
// MenuView.swift
// App
//
// Created by Thomas Stubblefield on 5/13/23.
//
import SwiftUI
struct MenuItemToggleView: View {
var tunnel: Tunnel
var body: some View {
HStack {
Text("Burrow")
.font(.headline)
Spacer()
Toggle("Burrow", isOn: tunnel.isOn)
.labelsHidden()
.disabled(tunnel.isDisabled)
.toggleStyle(.switch)
}
.padding(.horizontal, 4)
.padding(10)
.frame(minWidth: 300, minHeight: 32, maxHeight: 32)
}
}
extension Tunnel {
var isDisabled: Bool {
switch self.status {
case .disconnected, .permissionRequired, .connected:
return false
case .unknown, .disabled, .connecting, .reasserting, .disconnecting, .invalid, .configurationReadWriteFailed:
return true
}
}
var isOn: Binding<Bool> {
Binding {
switch self.status {
case .connecting, .reasserting, .connected:
true
default:
false
}
} set: { newValue in
switch (self.status, newValue) {
case (.permissionRequired, true):
Task { try await self.configure() }
case (.disconnected, true):
try? self.start()
case (.connected, false):
self.stop()
default:
return
}
}
}
}

View file

@ -0,0 +1,64 @@
//
// MenuItemToggleView.swift
// App
//
// Created by Thomas Stubblefield on 5/13/23.
//
import SwiftUI
struct MenuItemToggleView: View {
@Environment(\.tunnel)
var tunnel: Tunnel
var body: some View {
HStack {
VStack(alignment: .leading) {
Text("Burrow")
.font(.headline)
Text(tunnel.status.description)
.font(.subheadline)
}
Spacer()
Toggle(isOn: tunnel.toggleIsOn) {
}
.disabled(tunnel.toggleDisabled)
.toggleStyle(.switch)
}
.accessibilityElement(children: .combine)
.padding(.horizontal, 4)
.padding(10)
.frame(minWidth: 300, minHeight: 32, maxHeight: 32)
}
}
extension Tunnel {
fileprivate var toggleDisabled: Bool {
switch status {
case .disconnected, .permissionRequired, .connected, .disconnecting:
false
case .unknown, .disabled, .connecting, .reasserting, .invalid, .configurationReadWriteFailed:
true
}
}
var toggleIsOn: Binding<Bool> {
Binding {
switch status {
case .connecting, .reasserting, .connected:
true
default:
false
}
} set: { newValue in
switch (status, newValue) {
case (.permissionRequired, true):
enable()
case (_, true):
start()
case (_, false):
stop()
}
}
}
}

View file

@ -0,0 +1,167 @@
import BurrowShared
import NetworkExtension
@Observable
class NetworkExtensionTunnel: Tunnel {
@MainActor private(set) var status: TunnelStatus = .unknown
private var error: NEVPNError?
private let logger = Logger.logger(for: Tunnel.self)
private let bundleIdentifier: String
private var tasks: [Task<Void, Error>] = []
// Each manager corresponds to one entry in the Settings app.
// Our goal is to maintain a single manager, so we create one if none exist and delete any extra.
private var managers: [NEVPNManager]? {
didSet { Task { await updateStatus() } }
}
private var currentStatus: TunnelStatus {
guard let managers = managers else {
guard let error = error else {
return .unknown
}
switch error.code {
case .configurationReadWriteFailed:
return .configurationReadWriteFailed
default:
return .unknown
}
}
guard let manager = managers.first else {
return .permissionRequired
}
guard manager.isEnabled else {
return .disabled
}
return manager.connection.tunnelStatus
}
convenience init() {
self.init(Constants.networkExtensionBundleIdentifier)
}
init(_ bundleIdentifier: String) {
self.bundleIdentifier = bundleIdentifier
let center = NotificationCenter.default
let configurationChanged = Task { [weak self] in
for try await _ in center.notifications(named: .NEVPNConfigurationChange).map({ _ in () }) {
await self?.update()
}
}
let statusChanged = Task { [weak self] in
for try await _ in center.notifications(named: .NEVPNStatusDidChange).map({ _ in () }) {
await self?.updateStatus()
}
}
tasks = [configurationChanged, statusChanged]
Task { await update() }
}
private func update() async {
do {
managers = try await NETunnelProviderManager.managers
await self.updateStatus()
} catch let vpnError as NEVPNError {
error = vpnError
} catch {
logger.error("Failed to update VPN configurations: \(error)")
}
}
private func updateStatus() async {
await MainActor.run {
status = currentStatus
}
}
func configure() async throws {
if managers == nil {
await update()
}
guard let managers = managers else { return }
if managers.count > 1 {
try await withThrowingTaskGroup(of: Void.self, returning: Void.self) { group in
for manager in managers.suffix(from: 1) {
group.addTask { try await manager.remove() }
}
try await group.waitForAll()
}
}
guard managers.isEmpty else { return }
let manager = NETunnelProviderManager()
manager.localizedDescription = "Burrow"
let proto = NETunnelProviderProtocol()
proto.providerBundleIdentifier = bundleIdentifier
proto.serverAddress = "hackclub.com"
manager.protocolConfiguration = proto
try await manager.save()
}
func start() {
guard let manager = managers?.first else { return }
Task {
do {
if !manager.isEnabled {
manager.isEnabled = true
try await manager.save()
}
try manager.connection.startVPNTunnel()
} catch {
logger.error("Failed to start: \(error)")
}
}
}
func stop() {
guard let manager = managers?.first else { return }
manager.connection.stopVPNTunnel()
}
func enable() {
Task {
do {
try await configure()
} catch {
logger.error("Failed to enable: \(error)")
}
}
}
deinit {
tasks.forEach { $0.cancel() }
}
}
extension NEVPNConnection {
fileprivate var tunnelStatus: TunnelStatus {
switch status {
case .connected:
.connected(connectedDate!)
case .connecting:
.connecting
case .disconnecting:
.disconnecting
case .disconnected:
.disconnected
case .reasserting:
.reasserting
case .invalid:
.invalid
@unknown default:
.unknown
}
}
}

View file

@ -0,0 +1,88 @@
import SwiftUI
struct NetworkView<Content: View>: View {
var color: Color
var content: () -> Content
private var gradient: LinearGradient {
LinearGradient(
colors: [
color.opacity(0.8),
color
],
startPoint: .init(x: 0.2, y: 0),
endPoint: .init(x: 0.8, y: 1)
)
}
var body: some View {
content()
.frame(maxWidth: .infinity, minHeight: 175, maxHeight: 175)
.background(
RoundedRectangle(cornerRadius: 10)
.fill(gradient)
.background(
RoundedRectangle(cornerRadius: 10)
.fill(.white)
)
)
.shadow(color: .black.opacity(0.1), radius: 3.0, x: 0, y: 2)
}
}
struct AddNetworkView: View {
var body: some View {
Text("Add Network")
.frame(maxWidth: .infinity, minHeight: 175, maxHeight: 175)
.background(
RoundedRectangle(cornerRadius: 10)
.stroke(style: .init(lineWidth: 2, dash: [6]))
)
}
}
extension NetworkView where Content == AnyView {
init(network: any Network) {
color = network.backgroundColor
content = { AnyView(network.label) }
}
}
struct NetworkCarouselView: View {
var networks: [any Network] = [
HackClub(id: "1"),
HackClub(id: "2"),
WireGuard(id: "4"),
HackClub(id: "5"),
]
var body: some View {
ScrollView(.horizontal) {
LazyHStack {
ForEach(networks, id: \.id) { network in
NetworkView(network: network)
.containerRelativeFrame(.horizontal, count: 10, span: 7, spacing: 0, alignment: .center)
.scrollTransition(.interactive, axis: .horizontal) { content, phase in
content
.scaleEffect(1.0 - abs(phase.value) * 0.1)
}
}
AddNetworkView()
}
.scrollTargetLayout()
}
.scrollClipDisabled()
.scrollIndicators(.hidden)
.defaultScrollAnchor(.center)
.scrollTargetBehavior(.viewAligned)
.containerRelativeFrame(.horizontal)
}
}
#if DEBUG
struct NetworkCarouselView_Previews: PreviewProvider {
static var previews: some View {
NetworkCarouselView()
}
}
#endif

View file

@ -0,0 +1,23 @@
import SwiftUI
struct HackClub: Network {
var id: String
var backgroundColor: Color { .init("HackClub") }
var label: some View {
GeometryReader { reader in
VStack(alignment: .leading) {
Image("HackClub")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: reader.size.height / 4)
Spacer()
Text("@conradev")
.foregroundStyle(.white)
.font(.body.monospaced())
}
.padding()
.frame(maxWidth: .infinity)
}
}
}

View file

@ -0,0 +1,10 @@
import SwiftUI
protocol Network {
associatedtype Label: View
var id: String { get }
var backgroundColor: Color { get }
var label: Label { get }
}

View file

@ -0,0 +1,30 @@
import SwiftUI
struct WireGuard: Network {
var id: String
var backgroundColor: Color { .init("WireGuard") }
var label: some View {
GeometryReader { reader in
VStack(alignment: .leading) {
HStack {
Image("WireGuard")
.resizable()
.aspectRatio(contentMode: .fit)
Image("WireGuardTitle")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: reader.size.width / 2)
Spacer()
}
.frame(maxWidth: .infinity, maxHeight: reader.size.height / 4)
Spacer()
Text("@conradev")
.foregroundStyle(.white)
.font(.body.monospaced())
}
.padding()
.frame(maxWidth: .infinity)
}
}
}

View file

@ -1,42 +0,0 @@
import Foundation
import NetworkExtension
extension Tunnel {
enum Status: CustomStringConvertible, Equatable, Hashable {
case unknown
case permissionRequired
case disabled
case connecting
case connected(Date)
case disconnecting
case disconnected
case reasserting
case invalid
case configurationReadWriteFailed
var description: String {
switch self {
case .unknown:
return "Unknown"
case .permissionRequired:
return "Permission Required"
case .disconnected:
return "Disconnected"
case .disabled:
return "Disabled"
case .connecting:
return "Connecting"
case .connected:
return "Connected"
case .disconnecting:
return "Disconnecting"
case .reasserting:
return "Reasserting"
case .invalid:
return "Invalid"
case .configurationReadWriteFailed:
return "System Error"
}
}
}
}

View file

@ -1,146 +1,50 @@
import BurrowShared
import NetworkExtension
import SwiftUI import SwiftUI
protocol Tunnel {
var status: TunnelStatus { get }
func start()
func stop()
func enable()
}
enum TunnelStatus: Equatable, Hashable {
case unknown
case permissionRequired
case disabled
case connecting
case connected(Date)
case disconnecting
case disconnected
case reasserting
case invalid
case configurationReadWriteFailed
}
struct TunnelKey: EnvironmentKey {
static let defaultValue: any Tunnel = NetworkExtensionTunnel()
}
extension EnvironmentValues {
var tunnel: any Tunnel {
get { self[TunnelKey.self] }
set { self[TunnelKey.self] = newValue }
}
}
#if DEBUG
@Observable @Observable
class Tunnel { class PreviewTunnel: Tunnel {
private(set) var status: Status = .unknown var status: TunnelStatus = .permissionRequired
private var error: NEVPNError?
private let logger = Logger.logger(for: Tunnel.self) func start() {
private let bundleIdentifier: String status = .connected(.now)
private let configure: (NETunnelProviderManager, NETunnelProviderProtocol) -> Void
private var tasks: [Task<Void, Error>] = []
// Each manager corresponds to one entry in the Settings app.
// Our goal is to maintain a single manager, so we create one if none exist and delete extra if there are any.
private var managers: [NEVPNManager]? {
didSet { status = currentStatus }
} }
private var currentStatus: Status {
guard let managers = managers else {
guard let error = error else {
return .unknown
}
switch error.code {
case .configurationReadWriteFailed:
return .configurationReadWriteFailed
default:
return .unknown
}
}
guard let manager = managers.first else {
return .permissionRequired
}
guard manager.isEnabled else {
return .disabled
}
return manager.connection.tunnelStatus
}
convenience init(configure: @escaping (NETunnelProviderManager, NETunnelProviderProtocol) -> Void) {
self.init("com.hackclub.burrow.network", configure: configure)
}
init(_ bundleIdentifier: String, configure: @escaping (NETunnelProviderManager, NETunnelProviderProtocol) -> Void) {
self.bundleIdentifier = bundleIdentifier
self.configure = configure
let center = NotificationCenter.default
let configurationChanged = Task {
for try await _ in center.notifications(named: .NEVPNConfigurationChange).map({ _ in () }) {
await update()
}
}
let statusChanged = Task {
for try await _ in center.notifications(named: .NEVPNStatusDidChange).map({ _ in () }) {
await MainActor.run {
status = currentStatus
}
}
}
tasks = [configurationChanged, statusChanged]
Task { await update() }
}
private func update() async {
do {
let updated = try await NETunnelProviderManager.managers
await MainActor.run {
managers = updated
}
} catch let vpnError as NEVPNError {
error = vpnError
} catch {
logger.error("Failed to update VPN configurations: \(error)")
}
}
func configure() async throws {
if managers == nil {
await update()
}
guard let managers = managers else { return }
if managers.count > 1 {
try await withThrowingTaskGroup(of: Void.self, returning: Void.self) { group in
for manager in managers.suffix(from: 1) {
group.addTask { try await manager.remove() }
}
try await group.waitForAll()
}
}
if managers.isEmpty {
let manager = NETunnelProviderManager()
let proto = NETunnelProviderProtocol()
proto.providerBundleIdentifier = bundleIdentifier
configure(manager, proto)
manager.protocolConfiguration = proto
try await manager.save()
}
}
func start() throws {
guard let manager = managers?.first else { return }
try manager.connection.startVPNTunnel()
}
func stop() { func stop() {
guard let manager = managers?.first else { return } status = .disconnected
manager.connection.stopVPNTunnel()
} }
func enable() {
deinit { status = .disconnected
tasks.forEach { $0.cancel() }
}
}
extension NEVPNConnection {
var tunnelStatus: Tunnel.Status {
switch status {
case .connected:
.connected(connectedDate!)
case .connecting:
.connecting
case .disconnecting:
.disconnecting
case .disconnected:
.disconnected
case .reasserting:
.reasserting
case .invalid:
.invalid
@unknown default:
.unknown
}
} }
} }
#endif

View file

@ -0,0 +1,61 @@
import SwiftUI
struct TunnelButton: View {
@Environment(\.tunnel)
var tunnel: any Tunnel
var body: some View {
if let action = tunnel.action {
Button {
tunnel.perform(action)
} label: {
Text(action.description)
}
.padding(.horizontal)
.buttonStyle(.floating)
}
}
}
extension Tunnel {
fileprivate var action: TunnelButton.Action? {
switch status {
case .permissionRequired, .invalid:
.enable
case .disabled, .disconnecting, .disconnected:
.start
case .connecting, .connected, .reasserting:
.stop
case .unknown, .configurationReadWriteFailed:
nil
}
}
}
extension TunnelButton {
fileprivate enum Action {
case enable
case start
case stop
}
}
extension TunnelButton.Action {
var description: LocalizedStringKey {
switch self {
case .enable: "Enable"
case .start: "Start"
case .stop: "Stop"
}
}
}
extension Tunnel {
fileprivate func perform(_ action: TunnelButton.Action) {
switch action {
case .enable: enable()
case .start: start()
case .stop: stop()
}
}
}

View file

@ -0,0 +1,37 @@
import SwiftUI
struct TunnelStatusView: View {
@Environment(\.tunnel)
var tunnel: any Tunnel
var body: some View {
Text(tunnel.status.description)
}
}
extension TunnelStatus: CustomStringConvertible {
var description: String {
switch self {
case .unknown:
"Unknown"
case .permissionRequired:
"Permission Required"
case .disconnected:
"Disconnected"
case .disabled:
"Disabled"
case .connecting:
"Connecting…"
case .connected:
"Connected"
case .disconnecting:
"Disconnecting…"
case .reasserting:
"Reasserting…"
case .invalid:
"Invalid"
case .configurationReadWriteFailed:
"System Error"
}
}
}

View file

@ -1,34 +0,0 @@
import SwiftUI
struct TunnelView: View {
var tunnel: Tunnel
var body: some View {
VStack {
Text(verbatim: tunnel.status.description)
switch tunnel.status {
case .connected:
Button("Disconnect", action: stop)
case .permissionRequired:
Button("Allow", action: configure)
case .disconnected:
Button("Start", action: start)
default:
EmptyView()
}
}
.padding()
}
private func start() {
try? tunnel.start()
}
private func stop() {
tunnel.stop()
}
private func configure() {
Task { try await tunnel.configure() }
}
}

View file

@ -9,24 +9,32 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
0B28F1562ABF463A000D44B0 /* DataTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B28F1552ABF463A000D44B0 /* DataTypes.swift */; }; 0B28F1562ABF463A000D44B0 /* DataTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B28F1552ABF463A000D44B0 /* DataTypes.swift */; };
0B46E8E02AC918CA00BA2A3C /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B46E8DF2AC918CA00BA2A3C /* Client.swift */; }; 0B46E8E02AC918CA00BA2A3C /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B46E8DF2AC918CA00BA2A3C /* Client.swift */; };
43AA26D82A10004900F14CE6 /* MenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43AA26D72A10004900F14CE6 /* MenuView.swift */; }; 43AA26D82A10004900F14CE6 /* MenuItemToggleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43AA26D72A10004900F14CE6 /* MenuItemToggleView.swift */; };
D00117312B2FFFC900D87C25 /* NWConnection+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00117302B2FFFC900D87C25 /* NWConnection+Async.swift */; }; D00117312B2FFFC900D87C25 /* NWConnection+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00117302B2FFFC900D87C25 /* NWConnection+Async.swift */; };
D00117332B3001A400D87C25 /* NewlineProtocolFramer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00117322B3001A400D87C25 /* NewlineProtocolFramer.swift */; }; D00117332B3001A400D87C25 /* NewlineProtocolFramer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00117322B3001A400D87C25 /* NewlineProtocolFramer.swift */; };
D001173B2B30341C00D87C25 /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D001173A2B30341C00D87C25 /* Logging.swift */; }; D001173B2B30341C00D87C25 /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D001173A2B30341C00D87C25 /* Logging.swift */; };
D00117442B30372900D87C25 /* libBurrowShared.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D00117382B30341C00D87C25 /* libBurrowShared.a */; }; D00117442B30372900D87C25 /* libBurrowShared.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D00117382B30341C00D87C25 /* libBurrowShared.a */; };
D00117452B30372C00D87C25 /* libBurrowShared.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D00117382B30341C00D87C25 /* libBurrowShared.a */; }; D00117452B30372C00D87C25 /* libBurrowShared.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D00117382B30341C00D87C25 /* libBurrowShared.a */; };
D00AA8972A4669BC005C8102 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00AA8962A4669BC005C8102 /* AppDelegate.swift */; }; D00AA8972A4669BC005C8102 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00AA8962A4669BC005C8102 /* AppDelegate.swift */; };
D01A79312B81630D0024EC91 /* NetworkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01A79302B81630D0024EC91 /* NetworkView.swift */; };
D020F65829E4A697002790F6 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D020F65729E4A697002790F6 /* PacketTunnelProvider.swift */; }; D020F65829E4A697002790F6 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D020F65729E4A697002790F6 /* PacketTunnelProvider.swift */; };
D020F65D29E4A697002790F6 /* BurrowNetworkExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D020F65329E4A697002790F6 /* BurrowNetworkExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; D020F65D29E4A697002790F6 /* BurrowNetworkExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D020F65329E4A697002790F6 /* BurrowNetworkExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
D032E6522B8A79C20006B8AD /* HackClub.swift in Sources */ = {isa = PBXBuildFile; fileRef = D032E6512B8A79C20006B8AD /* HackClub.swift */; };
D032E6542B8A79DA0006B8AD /* WireGuard.swift in Sources */ = {isa = PBXBuildFile; fileRef = D032E6532B8A79DA0006B8AD /* WireGuard.swift */; };
D05B9F7629E39EEC008CB1F9 /* BurrowApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */; }; D05B9F7629E39EEC008CB1F9 /* BurrowApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */; };
D05B9F7829E39EEC008CB1F9 /* TunnelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05B9F7729E39EEC008CB1F9 /* TunnelView.swift */; }; D05B9F7829E39EEC008CB1F9 /* BurrowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05B9F7729E39EEC008CB1F9 /* BurrowView.swift */; };
D05B9F7A29E39EED008CB1F9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D05B9F7929E39EED008CB1F9 /* Assets.xcassets */; }; D05B9F7A29E39EED008CB1F9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D05B9F7929E39EED008CB1F9 /* Assets.xcassets */; };
D05EF8C82B81818D0017AB4F /* FloatingButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05EF8C72B81818D0017AB4F /* FloatingButtonStyle.swift */; };
D08252762B5C9FC4005DA378 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08252752B5C9FC4005DA378 /* Constants.swift */; }; D08252762B5C9FC4005DA378 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08252752B5C9FC4005DA378 /* Constants.swift */; };
D09150422B9D2AF700BE3CB0 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = D09150412B9D2AF700BE3CB0 /* MainMenu.xib */; };
D0BCC5FD2A086D4700AD070D /* NetworkExtension+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */; }; D0BCC5FD2A086D4700AD070D /* NetworkExtension+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */; };
D0BCC5FF2A086E1C00AD070D /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BCC5FE2A086E1C00AD070D /* Status.swift */; };
D0BCC6082A0981FE00AD070D /* Tunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B98FC629FDC5B5004E7149 /* Tunnel.swift */; }; D0BCC6082A0981FE00AD070D /* Tunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B98FC629FDC5B5004E7149 /* Tunnel.swift */; };
D0BCC6092A09A03E00AD070D /* libburrow.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0BCC6032A09535900AD070D /* libburrow.a */; }; D0BCC6092A09A03E00AD070D /* libburrow.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0BCC6032A09535900AD070D /* libburrow.a */; };
D0BCC60A2A09A0B800AD070D /* build-rust.sh in Resources */ = {isa = PBXBuildFile; fileRef = D0B98FBF29FD8072004E7149 /* build-rust.sh */; }; D0BCC60A2A09A0B800AD070D /* build-rust.sh in Resources */ = {isa = PBXBuildFile; fileRef = D0B98FBF29FD8072004E7149 /* build-rust.sh */; };
D0FAB5922B818A5900F6A84B /* NetworkExtensionTunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FAB5912B818A5900F6A84B /* NetworkExtensionTunnel.swift */; };
D0FAB5962B818B2900F6A84B /* TunnelButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FAB5952B818B2900F6A84B /* TunnelButton.swift */; };
D0FAB5982B818B8200F6A84B /* TunnelStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FAB5972B818B8200F6A84B /* TunnelStatusView.swift */; };
D0FAB59A2B818B9600F6A84B /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FAB5992B818B9600F6A84B /* Network.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@ -70,7 +78,7 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
0B28F1552ABF463A000D44B0 /* DataTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataTypes.swift; sourceTree = "<group>"; }; 0B28F1552ABF463A000D44B0 /* DataTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataTypes.swift; sourceTree = "<group>"; };
0B46E8DF2AC918CA00BA2A3C /* Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = "<group>"; }; 0B46E8DF2AC918CA00BA2A3C /* Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = "<group>"; };
43AA26D72A10004900F14CE6 /* MenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuView.swift; sourceTree = "<group>"; }; 43AA26D72A10004900F14CE6 /* MenuItemToggleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuItemToggleView.swift; sourceTree = "<group>"; };
D00117302B2FFFC900D87C25 /* NWConnection+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NWConnection+Async.swift"; sourceTree = "<group>"; }; D00117302B2FFFC900D87C25 /* NWConnection+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NWConnection+Async.swift"; sourceTree = "<group>"; };
D00117322B3001A400D87C25 /* NewlineProtocolFramer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewlineProtocolFramer.swift; sourceTree = "<group>"; }; D00117322B3001A400D87C25 /* NewlineProtocolFramer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewlineProtocolFramer.swift; sourceTree = "<group>"; };
D00117382B30341C00D87C25 /* libBurrowShared.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libBurrowShared.a; sourceTree = BUILT_PRODUCTS_DIR; }; D00117382B30341C00D87C25 /* libBurrowShared.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libBurrowShared.a; sourceTree = BUILT_PRODUCTS_DIR; };
@ -78,6 +86,7 @@
D00117412B30347800D87C25 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; }; D00117412B30347800D87C25 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
D00117422B30348D00D87C25 /* Shared.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = "<group>"; }; D00117422B30348D00D87C25 /* Shared.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = "<group>"; };
D00AA8962A4669BC005C8102 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; D00AA8962A4669BC005C8102 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
D01A79302B81630D0024EC91 /* NetworkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkView.swift; sourceTree = "<group>"; };
D020F63D29E4A1FF002790F6 /* Identity.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Identity.xcconfig; sourceTree = "<group>"; }; D020F63D29E4A1FF002790F6 /* Identity.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Identity.xcconfig; sourceTree = "<group>"; };
D020F64029E4A1FF002790F6 /* Compiler.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Compiler.xcconfig; sourceTree = "<group>"; }; D020F64029E4A1FF002790F6 /* Compiler.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Compiler.xcconfig; sourceTree = "<group>"; };
D020F64229E4A1FF002790F6 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; D020F64229E4A1FF002790F6 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -92,19 +101,26 @@
D020F66729E4A95D002790F6 /* NetworkExtension-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "NetworkExtension-iOS.entitlements"; sourceTree = "<group>"; }; D020F66729E4A95D002790F6 /* NetworkExtension-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "NetworkExtension-iOS.entitlements"; sourceTree = "<group>"; };
D020F66829E4AA74002790F6 /* App-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "App-iOS.entitlements"; sourceTree = "<group>"; }; D020F66829E4AA74002790F6 /* App-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "App-iOS.entitlements"; sourceTree = "<group>"; };
D020F66929E4AA74002790F6 /* App-macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "App-macOS.entitlements"; sourceTree = "<group>"; }; D020F66929E4AA74002790F6 /* App-macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "App-macOS.entitlements"; sourceTree = "<group>"; };
D032E6512B8A79C20006B8AD /* HackClub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HackClub.swift; sourceTree = "<group>"; };
D032E6532B8A79DA0006B8AD /* WireGuard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WireGuard.swift; sourceTree = "<group>"; };
D05B9F7229E39EEC008CB1F9 /* Burrow.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Burrow.app; sourceTree = BUILT_PRODUCTS_DIR; }; D05B9F7229E39EEC008CB1F9 /* Burrow.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Burrow.app; sourceTree = BUILT_PRODUCTS_DIR; };
D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurrowApp.swift; sourceTree = "<group>"; }; D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurrowApp.swift; sourceTree = "<group>"; };
D05B9F7729E39EEC008CB1F9 /* TunnelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelView.swift; sourceTree = "<group>"; }; D05B9F7729E39EEC008CB1F9 /* BurrowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurrowView.swift; sourceTree = "<group>"; };
D05B9F7929E39EED008CB1F9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; D05B9F7929E39EED008CB1F9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
D05EF8C72B81818D0017AB4F /* FloatingButtonStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FloatingButtonStyle.swift; sourceTree = "<group>"; };
D08252742B5C9DEB005DA378 /* Constants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Constants.h; sourceTree = "<group>"; }; D08252742B5C9DEB005DA378 /* Constants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Constants.h; sourceTree = "<group>"; };
D08252752B5C9FC4005DA378 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; }; D08252752B5C9FC4005DA378 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
D09150412B9D2AF700BE3CB0 /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = "<group>"; };
D0B98FBF29FD8072004E7149 /* build-rust.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "build-rust.sh"; sourceTree = "<group>"; }; D0B98FBF29FD8072004E7149 /* build-rust.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "build-rust.sh"; sourceTree = "<group>"; };
D0B98FC629FDC5B5004E7149 /* Tunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tunnel.swift; sourceTree = "<group>"; }; D0B98FC629FDC5B5004E7149 /* Tunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tunnel.swift; sourceTree = "<group>"; };
D0B98FD829FDDB6F004E7149 /* libburrow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = libburrow.h; sourceTree = "<group>"; }; D0B98FD829FDDB6F004E7149 /* libburrow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = libburrow.h; sourceTree = "<group>"; };
D0B98FDC29FDDDCF004E7149 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; }; D0B98FDC29FDDDCF004E7149 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NetworkExtension+Async.swift"; sourceTree = "<group>"; }; D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NetworkExtension+Async.swift"; sourceTree = "<group>"; };
D0BCC5FE2A086E1C00AD070D /* Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Status.swift; sourceTree = "<group>"; };
D0BCC6032A09535900AD070D /* libburrow.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libburrow.a; sourceTree = BUILT_PRODUCTS_DIR; }; D0BCC6032A09535900AD070D /* libburrow.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libburrow.a; sourceTree = BUILT_PRODUCTS_DIR; };
D0FAB5912B818A5900F6A84B /* NetworkExtensionTunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkExtensionTunnel.swift; sourceTree = "<group>"; };
D0FAB5952B818B2900F6A84B /* TunnelButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelButton.swift; sourceTree = "<group>"; };
D0FAB5972B818B8200F6A84B /* TunnelStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelStatusView.swift; sourceTree = "<group>"; };
D0FAB5992B818B9600F6A84B /* Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -135,14 +151,6 @@
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
43AA26D62A0FFFD000F14CE6 /* Menu */ = {
isa = PBXGroup;
children = (
43AA26D72A10004900F14CE6 /* MenuView.swift */,
);
path = Menu;
sourceTree = "<group>";
};
D00117392B30341C00D87C25 /* Shared */ = { D00117392B30341C00D87C25 /* Shared */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -199,6 +207,16 @@
path = NetworkExtension; path = NetworkExtension;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D032E64D2B8A69C90006B8AD /* Networks */ = {
isa = PBXGroup;
children = (
D0FAB5992B818B9600F6A84B /* Network.swift */,
D032E6512B8A79C20006B8AD /* HackClub.swift */,
D032E6532B8A79DA0006B8AD /* WireGuard.swift */,
);
path = Networks;
sourceTree = "<group>";
};
D05B9F6929E39EEC008CB1F9 = { D05B9F6929E39EEC008CB1F9 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -224,14 +242,20 @@
D05B9F7429E39EEC008CB1F9 /* App */ = { D05B9F7429E39EEC008CB1F9 /* App */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
43AA26D62A0FFFD000F14CE6 /* Menu */,
D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */, D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */,
D00AA8962A4669BC005C8102 /* AppDelegate.swift */, D00AA8962A4669BC005C8102 /* AppDelegate.swift */,
D05B9F7729E39EEC008CB1F9 /* TunnelView.swift */, 43AA26D72A10004900F14CE6 /* MenuItemToggleView.swift */,
D05B9F7729E39EEC008CB1F9 /* BurrowView.swift */,
D01A79302B81630D0024EC91 /* NetworkView.swift */,
D032E64D2B8A69C90006B8AD /* Networks */,
D0FAB5972B818B8200F6A84B /* TunnelStatusView.swift */,
D0FAB5952B818B2900F6A84B /* TunnelButton.swift */,
D0B98FC629FDC5B5004E7149 /* Tunnel.swift */, D0B98FC629FDC5B5004E7149 /* Tunnel.swift */,
D0BCC5FE2A086E1C00AD070D /* Status.swift */, D0FAB5912B818A5900F6A84B /* NetworkExtensionTunnel.swift */,
D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */, D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */,
D05EF8C72B81818D0017AB4F /* FloatingButtonStyle.swift */,
D05B9F7929E39EED008CB1F9 /* Assets.xcassets */, D05B9F7929E39EED008CB1F9 /* Assets.xcassets */,
D09150412B9D2AF700BE3CB0 /* MainMenu.xib */,
D020F66829E4AA74002790F6 /* App-iOS.entitlements */, D020F66829E4AA74002790F6 /* App-iOS.entitlements */,
D020F66929E4AA74002790F6 /* App-macOS.entitlements */, D020F66929E4AA74002790F6 /* App-macOS.entitlements */,
D020F64929E4A34B002790F6 /* App.xcconfig */, D020F64929E4A34B002790F6 /* App.xcconfig */,
@ -369,6 +393,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
D05B9F7A29E39EED008CB1F9 /* Assets.xcassets in Resources */, D05B9F7A29E39EED008CB1F9 /* Assets.xcassets in Resources */,
D09150422B9D2AF700BE3CB0 /* MainMenu.xib in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -423,12 +448,19 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
D0FAB59A2B818B9600F6A84B /* Network.swift in Sources */,
D0BCC6082A0981FE00AD070D /* Tunnel.swift in Sources */, D0BCC6082A0981FE00AD070D /* Tunnel.swift in Sources */,
43AA26D82A10004900F14CE6 /* MenuView.swift in Sources */, D0FAB5982B818B8200F6A84B /* TunnelStatusView.swift in Sources */,
D05B9F7829E39EEC008CB1F9 /* TunnelView.swift in Sources */, 43AA26D82A10004900F14CE6 /* MenuItemToggleView.swift in Sources */,
D0BCC5FF2A086E1C00AD070D /* Status.swift in Sources */, D05B9F7829E39EEC008CB1F9 /* BurrowView.swift in Sources */,
D0FAB5922B818A5900F6A84B /* NetworkExtensionTunnel.swift in Sources */,
D0FAB5962B818B2900F6A84B /* TunnelButton.swift in Sources */,
D00AA8972A4669BC005C8102 /* AppDelegate.swift in Sources */, D00AA8972A4669BC005C8102 /* AppDelegate.swift in Sources */,
D05EF8C82B81818D0017AB4F /* FloatingButtonStyle.swift in Sources */,
D032E6522B8A79C20006B8AD /* HackClub.swift in Sources */,
D05B9F7629E39EEC008CB1F9 /* BurrowApp.swift in Sources */, D05B9F7629E39EEC008CB1F9 /* BurrowApp.swift in Sources */,
D01A79312B81630D0024EC91 /* NetworkView.swift in Sources */,
D032E6542B8A79DA0006B8AD /* WireGuard.swift in Sources */,
D0BCC5FD2A086D4700AD070D /* NetworkExtension+Async.swift in Sources */, D0BCC5FD2A086D4700AD070D /* NetworkExtension+Async.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -568,8 +600,8 @@
isa = XCRemoteSwiftPackageReference; isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/realm/SwiftLint.git"; repositoryURL = "https://github.com/realm/SwiftLint.git";
requirement = { requirement = {
kind = upToNextMajorVersion; branch = main;
minimumVersion = 0.54.0; kind = branch;
}; };
}; };
/* End XCRemoteSwiftPackageReference section */ /* End XCRemoteSwiftPackageReference section */

View file

@ -32,8 +32,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser.git", "location" : "https://github.com/apple/swift-argument-parser.git",
"state" : { "state" : {
"revision" : "fee6933f37fde9a5e12a1e4aeaa93fe60116ff2a", "revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531",
"version" : "1.2.2" "version" : "1.2.3"
} }
}, },
{ {
@ -41,8 +41,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-syntax.git", "location" : "https://github.com/apple/swift-syntax.git",
"state" : { "state" : {
"revision" : "6ad4ea24b01559dde0773e3d091f1b9e36175036", "revision" : "64889f0c732f210a935a0ad7cda38f77f876262d",
"version" : "509.0.2" "version" : "509.1.1"
} }
}, },
{ {
@ -50,8 +50,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/realm/SwiftLint.git", "location" : "https://github.com/realm/SwiftLint.git",
"state" : { "state" : {
"revision" : "f17a4f9dfb6a6afb0408426354e4180daaf49cee", "branch" : "main",
"version" : "0.54.0" "revision" : "7595ad3fafc1a31086dc40ba01fd898bf6b42d5f"
} }
}, },
{ {
@ -68,8 +68,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/drmohundro/SWXMLHash.git", "location" : "https://github.com/drmohundro/SWXMLHash.git",
"state" : { "state" : {
"revision" : "4d0f62f561458cbe1f732171e625f03195151b60", "revision" : "a853604c9e9a83ad9954c7e3d2a565273982471f",
"version" : "7.0.1" "version" : "7.0.2"
} }
}, },
{ {

View file

@ -6,10 +6,16 @@ import os
class PacketTunnelProvider: NEPacketTunnelProvider { class PacketTunnelProvider: NEPacketTunnelProvider {
private let logger = Logger.logger(for: PacketTunnelProvider.self) private let logger = Logger.logger(for: PacketTunnelProvider.self)
override func startTunnel(options: [String: NSObject]? = nil) async throws { override init() {
do { do {
libburrow.spawnInProcess(socketPath: try Constants.socketURL.path) libburrow.spawnInProcess(socketPath: try Constants.socketURL.path)
} catch {
logger.error("Failed to spawn: \(error)")
}
}
override func startTunnel(options: [String: NSObject]? = nil) async throws {
do {
let client = try Client() let client = try Client()
let command = BurrowRequest(id: 0, command: "ServerConfig") let command = BurrowRequest(id: 0, command: "ServerConfig")

View file

@ -7,6 +7,7 @@ public enum Constants {
public static let bundleIdentifier = AppBundleIdentifier public static let bundleIdentifier = AppBundleIdentifier
public static let appGroupIdentifier = AppGroupIdentifier public static let appGroupIdentifier = AppGroupIdentifier
public static let networkExtensionBundleIdentifier = NetworkExtensionBundleIdentifier
public static var groupContainerURL: URL { public static var groupContainerURL: URL {
get throws { try _groupContainerURL.get() } get throws { try _groupContainerURL.get() }

View file

@ -7,5 +7,6 @@ NS_ASSUME_NONNULL_BEGIN
static NSString * const AppBundleIdentifier = MACRO_STRING(APP_BUNDLE_IDENTIFIER); static NSString * const AppBundleIdentifier = MACRO_STRING(APP_BUNDLE_IDENTIFIER);
static NSString * const AppGroupIdentifier = MACRO_STRING(APP_GROUP_IDENTIFIER); static NSString * const AppGroupIdentifier = MACRO_STRING(APP_GROUP_IDENTIFIER);
static NSString * const NetworkExtensionBundleIdentifier = MACRO_STRING(NETWORK_EXTENSION_BUNDLE_IDENTIFIER);
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View file

@ -2,4 +2,4 @@ PRODUCT_NAME = BurrowShared
MERGEABLE_LIBRARY = YES MERGEABLE_LIBRARY = YES
SWIFT_INCLUDE_PATHS = $(PROJECT_DIR)/Shared/Constants SWIFT_INCLUDE_PATHS = $(PROJECT_DIR)/Shared/Constants
GCC_PREPROCESSOR_DEFINITIONS = APP_BUNDLE_IDENTIFIER=$(APP_BUNDLE_IDENTIFIER) APP_GROUP_IDENTIFIER=$(APP_GROUP_IDENTIFIER) GCC_PREPROCESSOR_DEFINITIONS = APP_BUNDLE_IDENTIFIER=$(APP_BUNDLE_IDENTIFIER) APP_GROUP_IDENTIFIER=$(APP_GROUP_IDENTIFIER) NETWORK_EXTENSION_BUNDLE_IDENTIFIER=$(NETWORK_EXTENSION_BUNDLE_IDENTIFIER)