diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..20016c6ad2b779261848a4d2b4d68e6f180d0d6c Binary files /dev/null and b/.DS_Store differ diff --git a/.swift-version b/.swift-version new file mode 100644 index 0000000000000000000000000000000000000000..43f030c74bdfec26c580393ddbd8439ff9c97b22 --- /dev/null +++ b/.swift-version @@ -0,0 +1 @@ +5.7 \ No newline at end of file diff --git a/ApplicationLibrary/ApplicationLibrary.swift b/ApplicationLibrary/ApplicationLibrary.swift new file mode 100644 index 0000000000000000000000000000000000000000..b8246da1f1a6578f691f61430acfd45a7eec7e35 --- /dev/null +++ b/ApplicationLibrary/ApplicationLibrary.swift @@ -0,0 +1,6 @@ +import Foundation + +public class ApplicationLibrary { + public static let bundle = Bundle(for: ApplicationLibrary.self) + public static let inPreview = false +} diff --git a/ApplicationLibrary/Assets.xcassets/Contents.json b/ApplicationLibrary/Assets.xcassets/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..73c00596a7fca3f3d4bdd64053b69d86745f9e10 --- /dev/null +++ b/ApplicationLibrary/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ApplicationLibrary/Assets.xcassets/save.symbolset/Contents.json b/ApplicationLibrary/Assets.xcassets/save.symbolset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..51af66865e7a8309ed8d06bcafcf0255090b5afb --- /dev/null +++ b/ApplicationLibrary/Assets.xcassets/save.symbolset/Contents.json @@ -0,0 +1,12 @@ +{ + "info": { + "author": "xcode", + "version": 1 + }, + "symbols": [ + { + "filename": "save-save_symbol.svg", + "idiom": "universal" + } + ] +} diff --git a/ApplicationLibrary/Assets.xcassets/save.symbolset/save-save_symbol.svg b/ApplicationLibrary/Assets.xcassets/save.symbolset/save-save_symbol.svg new file mode 100644 index 0000000000000000000000000000000000000000..2b67790d87ab2fade69c18228d7d0563e1223310 --- /dev/null +++ b/ApplicationLibrary/Assets.xcassets/save.symbolset/save-save_symbol.svg @@ -0,0 +1,127 @@ +<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="3300" height="2000"> + <g id="Notes"> + <style> + text { + font-family: monospace; + text-anchor: middle; + } + text.emphasis { + font-weight:bold; + } + text.lanchor { + text-anchor: start; + } + </style> + <rect height="2200" id="artboard" fill="white" width="3300" x="0" y="0"/> + <line id="" style="fill:none;stroke:black;stroke-width:0.5;" x1="263" x2="3036" y1="292" y2="292"/> + <text class="lanchor emphasis" transform="matrix(1 0 0 1 263 322)">Weight/Scale Variations</text> + <text transform="matrix(1 0 0 1 559.711 322)">Ultralight</text> + <text transform="matrix(1 0 0 1 856.422 322)">Thin</text> + <text transform="matrix(1 0 0 1 1153.13 322)">Light</text> + <text transform="matrix(1 0 0 1 1449.84 322)">Regular</text> + <text transform="matrix(1 0 0 1 1746.56 322)">Medium</text> + <text transform="matrix(1 0 0 1 2043.27 322)">Semibold</text> + <text transform="matrix(1 0 0 1 2339.98 322)">Bold</text> + <text transform="matrix(1 0 0 1 2636.69 322)">Heavy</text> + <text transform="matrix(1 0 0 1 2933.4 322)">Black</text> + <text id="template-version" style="text-anchor: end;" transform="matrix(1 0 0 1 3036 1933)">Template v.1.0 + </text> + <text class="lanchor" transform="matrix(1 0 0 1 263 726)">Small</text> + <text class="lanchor" transform="matrix(1 0 0 1 263 1156)">Medium</text> + <text class="lanchor" transform="matrix(1 0 0 1 263 1586)">Large</text> + </g> + <g id="Guides"> + <g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 696)"> + <path d="M 54.9316 0 L 57.666 0 L 30.5664 -70.459 L 28.0762 -70.459 L 0.976562 0 L 3.66211 0 L 12.9395 -24.4629 L 45.7031 -24.4629 Z M 29.1992 -67.0898 L 29.4434 -67.0898 L 44.8242 -26.709 L 13.8184 -26.709 Z"/> + </g> + <line id="Baseline-S" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.577;" x1="263" x2="3036" y1="696" + y2="696"/> + <line id="Capline-S" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.577;" x1="263" x2="3036" + y1="625.541" y2="625.541"/> + <g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 1126)"> + <path d="M 54.9316 0 L 57.666 0 L 30.5664 -70.459 L 28.0762 -70.459 L 0.976562 0 L 3.66211 0 L 12.9395 -24.4629 L 45.7031 -24.4629 Z M 29.1992 -67.0898 L 29.4434 -67.0898 L 44.8242 -26.709 L 13.8184 -26.709 Z"/> + </g> + <line id="Baseline-M" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.577;" x1="263" x2="3036" + y1="1126" y2="1126"/> + <line id="Capline-M" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.577;" x1="263" x2="3036" + y1="1055.54" y2="1055.54"/> + <g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 1556)"> + <path d="M 54.9316 0 L 57.666 0 L 30.5664 -70.459 L 28.0762 -70.459 L 0.976562 0 L 3.66211 0 L 12.9395 -24.4629 L 45.7031 -24.4629 Z M 29.1992 -67.0898 L 29.4434 -67.0898 L 44.8242 -26.709 L 13.8184 -26.709 Z"/> + </g> + <line id="Baseline-L" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.577;" x1="263" x2="3036" + y1="1556" y2="1556"/> + <line id="Capline-L" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.577;" x1="263" x2="3036" + y1="1485.54" y2="1485.54"/> + <rect height="119.336" id="left-margin" style="fill:darkred;stroke:none;opacity:0.4;" width="12.4512" + x="1394.06" y="1030.79"/> + <rect height="119.336" id="right-margin" style="fill:darkgreen;stroke:none;opacity:0.4;" width="12.4512" + x="1502.5112" y="1030.79"/> + <rect x="1406.5112" y="1045" width="96" height="96" fill="none" stroke="blue" stroke-dasharray="5"/> + <rect x="1394.06" y="1030.79" width="120.9024" height="119.336" fill="none" stroke="blue"/> + </g> + <g id="Symbols"> + <g id="Bold-L" transform="matrix(1 0 0 1 2266 1556)"> + <path d="M139.6425 -69.73749999999998 V11.693750000000016 Q139.6425 17.990562500000017 135.16474875 22.420906250000016 Q130.68715875 26.851250000000014 124.32375 26.851250000000014 H30.47625 Q24.1794375 26.851250000000014 19.74909375 22.420906250000016 Q15.31875 17.990562500000017 15.31875 11.693750000000016 V-82.15374999999999 Q15.31875 -88.51715875 19.74909375 -92.99474874999999 Q24.1794375 -97.4725 30.47625 -97.4725 H111.9075 L139.6425 -69.73749999999998 Z M124.32375 -62.96499999999999 105.135 -82.15374999999999 H30.47625 Q30.47625 -82.15374999999999 30.47625 -82.15374999999999 Q30.47625 -82.15374999999999 30.47625 -82.15374999999999 V11.693750000000016 Q30.47625 11.693750000000016 30.47625 11.693750000000016 Q30.47625 11.693750000000016 30.47625 11.693750000000016 H124.32375 Q124.32375 11.693750000000016 124.32375 11.693750000000016 Q124.32375 11.693750000000016 124.32375 11.693750000000016 V-62.96499999999999 Z M77.36210625 1.2125000000000128 Q84.33375000000001 1.2125000000000128 89.251875 -3.6677312499999815 Q94.17 -8.547801249999985 94.17 -15.519606249999981 Q94.17 -22.49124999999998 89.28976875000001 -27.409374999999983 Q84.40969875 -32.327499999999986 77.43789375 -32.327499999999986 Q70.46625 -32.327499999999986 65.548125 -27.447268749999992 Q60.63 -22.56719874999999 60.63 -15.595393749999985 Q60.63 -8.623749999999987 65.51023125 -3.7056249999999835 Q70.39030125000001 1.2125000000000128 77.36210625 1.2125000000000128 Z M39.0225 -50.548749999999984 H96.75 V-73.60749999999999 H39.0225 V-50.548749999999984 Z"/> + </g> + <g id="Semibold-L" transform="matrix(1 0 0 1 1970.27 1556)"> + <path d="M137.81973000000002 -68.96640249999999 V12.324721250000017 Q137.81973000000002 17.566475000000015 134.02696875 21.332468750000015 Q130.23404625 25.098623750000016 124.95472125 25.098623750000016 H29.84527875 Q24.603525 25.098623750000016 20.83753125 21.332468750000015 Q17.07137625 17.566475000000015 17.07137625 12.324721250000017 V-82.78472124999999 Q17.07137625 -88.06404624999999 20.83753125 -91.85696875 Q24.603525 -95.64973 29.84527875 -95.64973 H111.1364025 L137.81973000000002 -68.96640249999999 Z M124.95472125 -63.24541375 105.41541375000001 -82.78472124999999 H29.84527875 Q29.84527875 -82.78472124999999 29.84527875 -82.78472124999999 Q29.84527875 -82.78472124999999 29.84527875 -82.78472124999999 V12.324721250000017 Q29.84527875 12.324721250000017 29.84527875 12.324721250000017 Q29.84527875 12.324721250000017 29.84527875 12.324721250000017 H124.95472125 Q124.95472125 12.324721250000017 124.95472125 12.324721250000017 Q124.95472125 12.324721250000017 124.95472125 12.324721250000017 V-63.24541375 Z M77.36210625 1.8434712500000145 Q84.33375000000001 1.8434712500000145 89.251875 -3.0367599999999797 Q94.17 -7.916991249999988 94.17 -14.88863499999998 Q94.17 -21.860278749999978 89.28976875000001 -26.77840374999998 Q84.40969875 -31.696528749999985 77.43789375 -31.696528749999985 Q70.46625 -31.696528749999985 65.548125 -26.81645874999998 Q60.63 -21.936227499999987 60.63 -14.964583749999981 Q60.63 -7.992778749999985 65.51023125 -3.0746537499999818 Q70.39030125000001 1.8434712500000145 77.36210625 1.8434712500000145 Z M38.39152875 -51.179721249999986 H96.11902875 V-74.23847124999999 H38.39152875 V-51.179721249999986 Z"/> + </g> + <g id="Medium-L" transform="matrix(1 0 0 1 1674.31 1556)"> + <path d="M136.4526525 -68.38783749999999 V12.797990000000016 Q136.4526525 17.248328750000017 133.17331125 20.516221250000015 Q129.89413125 23.783952500000016 125.42799 23.783952500000016 H29.37201 Q24.92167125 23.783952500000016 21.65377875 20.516221250000015 Q18.3860475 17.248328750000017 18.3860475 12.797990000000016 V-83.25798999999998 Q18.3860475 -87.72413124999997 21.65377875 -91.00331125 Q24.92167125 -94.28265249999998 29.37201 -94.28265249999998 H110.5578375 L136.4526525 -68.38783749999999 Z M125.42799 -63.45568374999999 105.62568375000001 -83.25798999999998 H29.37201 Q29.37201 -83.25798999999998 29.37201 -83.25798999999998 Q29.37201 -83.25798999999998 29.37201 -83.25798999999998 V12.797990000000016 Q29.37201 12.797990000000016 29.37201 12.797990000000016 Q29.37201 12.797990000000016 29.37201 12.797990000000016 H125.42799 Q125.42799 12.797990000000016 125.42799 12.797990000000016 Q125.42799 12.797990000000016 125.42799 12.797990000000016 V-63.45568374999999 Z M77.36210625 2.316740000000017 Q84.33375000000001 2.316740000000017 89.251875 -2.5634912499999842 Q94.17 -7.443722499999986 94.17 -14.415366249999984 Q94.17 -21.387009999999982 89.28976875000001 -26.30513499999998 Q84.40969875 -31.223259999999982 77.43789375 -31.223259999999982 Q70.46625 -31.223259999999982 65.548125 -26.34318999999998 Q60.63 -21.462958749999984 60.63 -14.491314999999979 Q60.63 -7.519509999999983 65.51023125 -2.6013849999999863 Q70.39030125000001 2.316740000000017 77.36210625 2.316740000000017 Z M37.91826 -51.652989999999974 H95.64576000000001 V-74.71173999999998 H37.91826 V-51.652989999999974 Z"/> + </g> + <g id="Regular-L" transform="matrix(1 0 0 1 1378.56 1556)"> + <path d="M135.45000000000002 -67.96374999999999 V13.145000000000014 Q135.45000000000002 17.015000000000015 132.5475 19.917500000000015 Q129.645 22.820000000000014 125.775 22.820000000000014 H29.025000000000002 Q25.155 22.820000000000014 22.2525 19.917500000000015 Q19.35 17.015000000000015 19.35 13.145000000000014 V-83.60499999999999 Q19.35 -87.475 22.2525 -90.3775 Q25.155 -93.28 29.025000000000002 -93.28 H110.13375 L135.45000000000002 -67.96374999999999 Z M125.775 -63.609999999999985 105.78 -83.60499999999999 H29.025000000000002 Q29.025000000000002 -83.60499999999999 29.025000000000002 -83.60499999999999 Q29.025000000000002 -83.60499999999999 29.025000000000002 -83.60499999999999 V13.145000000000014 Q29.025000000000002 13.145000000000014 29.025000000000002 13.145000000000014 Q29.025000000000002 13.145000000000014 29.025000000000002 13.145000000000014 H125.775 Q125.775 13.145000000000014 125.775 13.145000000000014 Q125.775 13.145000000000014 125.775 13.145000000000014 V-63.609999999999985 Z M77.36210625 2.6637500000000145 Q84.33375000000001 2.6637500000000145 89.251875 -2.2164812499999798 Q94.17 -7.096551249999983 94.17 -14.068356249999987 Q94.17 -21.039999999999985 89.28976875000001 -25.95812499999998 Q84.40969875 -30.876249999999985 77.43789375 -30.876249999999985 Q70.46625 -30.876249999999985 65.548125 -25.99601874999999 Q60.63 -21.115948749999987 60.63 -14.14414374999999 Q60.63 -7.172499999999985 65.51023125 -2.254374999999982 Q70.39030125000001 2.6637500000000145 77.36210625 2.6637500000000145 Z M37.57125 -51.999999999999986 H95.29875 V-75.05874999999999 H37.57125 V-51.999999999999986 Z"/> + </g> + <g id="Light-L" transform="matrix(1 0 0 1 1082.77 1556)"> + <path d="M132.22483875 -66.47525124999999 V10.291842500000016 Q132.22483875 14.085893750000018 129.47036625 16.840366250000017 Q126.71589375 19.594838750000015 122.92184250000001 19.594838750000015 H31.8781575 Q28.084106249999998 19.594838750000015 25.32963375 16.840366250000017 Q22.57516125 14.085893750000018 22.57516125 10.291842500000016 V-80.7518425 Q22.57516125 -84.54589374999999 25.32963375 -87.30036625 Q28.084106249999998 -90.05483874999999 31.8781575 -90.05483874999999 H108.64525125 L132.22483875 -66.47525124999999 Z M124.90666875000001 -63.175753749999984 105.34575375 -82.73666874999999 H31.8781575 Q31.00982625 -82.73666874999999 30.451578750000003 -82.17842124999999 Q29.89333125 -81.62017374999999 29.89333125 -80.7518425 V10.291842500000016 Q29.89333125 11.160173750000016 30.451578750000003 11.718421250000013 Q31.00982625 12.276668750000017 31.8781575 12.276668750000017 H122.92184250000001 Q123.79017375000001 12.276668750000017 124.34842125 11.718421250000013 Q124.90666875000001 11.160173750000016 124.90666875000001 10.291842500000016 V-63.175753749999984 Z M77.36210625 -1.86398874999999 Q83.25353625000001 -1.86398874999999 87.44023125 -6.012789999999981 Q91.6270875 -10.16159124999998 91.6270875 -16.05302124999998 Q91.6270875 -21.944451249999986 87.478125 -26.13130749999999 Q83.32932375 -30.31800249999999 77.43789375 -30.31800249999999 Q71.54646375 -30.31800249999999 67.35976875 -26.169201249999986 Q63.172912499999995 -22.02039999999998 63.172912499999995 -16.12896999999998 Q63.172912499999995 -10.237539999999981 67.321875 -6.050683749999983 Q71.47067625 -1.86398874999999 77.36210625 -1.86398874999999 Z M40.36232625 -51.68991624999998 H93.06592125 V-72.26767374999999 H40.36232625 V-51.68991624999998 Z"/> + </g> + <g id="Thin-L" transform="matrix(1 0 0 1 787.282 1556)"> + <path d="M129.0 -64.98691374999999 V7.4393300000000195 Q129.0 11.157271250000015 126.39371625 13.763716250000012 Q123.78727125 16.370000000000015 120.06933000000001 16.370000000000015 H34.730669999999996 Q31.01272875 16.370000000000015 28.406283750000004 13.763716250000012 Q25.8 11.157271250000015 25.8 7.4393300000000195 V-77.89932999999999 Q25.8 -81.61727124999999 28.406283750000004 -84.22371624999998 Q31.01272875 -86.82999999999998 34.730669999999996 -86.82999999999998 H107.15691375 L129.0 -64.98691374999999 Z M124.03849875 -62.74166874999999 104.91166875 -81.86849874999999 H34.730669999999996 Q32.99416875 -81.86849874999999 31.877835 -80.75216499999999 Q30.761501250000002 -79.63583124999998 30.761501250000002 -77.89932999999999 V7.4393300000000195 Q30.761501250000002 9.175831250000016 31.877835 10.292165000000015 Q32.99416875 11.408498750000014 34.730669999999996 11.408498750000014 H120.06933000000001 Q121.80583125 11.408498750000014 122.922165 10.292165000000015 Q124.03849875 9.175831250000016 124.03849875 7.4393300000000195 V-62.74166874999999 Z M77.36210625 -6.391082499999982 Q82.17348375 -6.391082499999982 85.62891 -9.808614999999989 Q89.0844975 -13.226147499999982 89.0844975 -18.037524999999988 Q89.0844975 -22.848902499999994 85.66680375000001 -26.304328749999982 Q82.24927124999999 -29.75991624999999 77.43789375 -29.75991624999999 Q72.62651625 -29.75991624999999 69.17109 -26.342383749999982 Q65.7155025 -22.924689999999984 65.7155025 -18.113473749999983 Q65.7155025 -13.302096249999984 69.13319625 -9.846508749999991 Q72.55072875 -6.391082499999982 77.36210625 -6.391082499999982 Z M43.152918750000005 -51.37983249999999 H90.833415 V-69.47708124999998 H43.152918750000005 V-51.37983249999999 Z"/> + </g> + <g id="Ultralight-L" transform="matrix(1 0 0 1 491.205 1556)"> + <path d="M127.065 -64.09374999999999 V5.727500000000013 Q127.065 9.399968750000014 124.547565 11.917403750000016 Q122.02996875 14.435000000000016 118.3575 14.435000000000016 H36.4425 Q32.77003125 14.435000000000016 30.25259625 11.917403750000016 Q27.735 9.399968750000014 27.735 5.727500000000013 V-76.18749999999999 Q27.735 -79.85996874999998 30.25259625 -82.37756499999999 Q32.77003125 -84.89499999999998 36.4425 -84.89499999999998 H106.26375 L127.065 -64.09374999999999 Z M123.5175 -62.48124999999999 104.65125 -81.34749999999998 H36.4425 Q34.185 -81.34749999999998 32.73375 -79.89624999999998 Q31.282500000000002 -78.445 31.282500000000002 -76.18749999999999 V5.727500000000013 Q31.282500000000002 7.985000000000014 32.73375 9.436250000000015 Q34.185 10.887500000000014 36.4425 10.887500000000014 H118.3575 Q120.61500000000001 10.887500000000014 122.06625 9.436250000000015 Q123.5175 7.985000000000014 123.5175 5.727500000000013 V-62.48124999999999 Z M77.36210625 -9.107499999999987 Q81.52525875 -9.107499999999987 84.542085 -12.086271249999989 Q87.55875 -15.06504249999999 87.55875 -19.228356249999983 Q87.55875 -23.391508749999986 84.57997875000001 -26.40833499999998 Q81.6012075 -29.424999999999983 77.43789375 -29.424999999999983 Q73.27474124999999 -29.424999999999983 70.25791500000001 -26.44622874999999 Q67.24125000000001 -23.46745749999998 67.24125000000001 -19.304143749999987 Q67.24125000000001 -15.140991249999985 70.22002125 -12.12416499999999 Q73.19879250000001 -9.107499999999987 77.36210625 -9.107499999999987 Z M44.8275 -51.19374999999998 H89.49375 V-67.80249999999998 H44.8275 V-51.19374999999998 Z"/> + </g> + <g id="Bold-M" transform="matrix(1 0 0 1 2281.8 1126)"> + <path d="M109.25 -61.97999999999999 V-1.7299999999999898 Q109.25 4.89500000000001 104.6875 9.45750000000001 Q100.125 14.02000000000001 93.5 14.02000000000001 H26.5 Q19.875 14.02000000000001 15.3125 9.45750000000001 Q10.75 4.89500000000001 10.75 -1.7299999999999898 V-68.72999999999999 Q10.75 -75.35499999999999 15.3125 -79.91749999999999 Q19.875 -84.47999999999999 26.5 -84.47999999999999 H86.75 L109.25 -61.97999999999999 Z M93.5 -55.35499999999999 80.125 -68.72999999999999 H26.5 Q26.5 -68.72999999999999 26.5 -68.72999999999999 Q26.5 -68.72999999999999 26.5 -68.72999999999999 V-1.7299999999999898 Q26.5 -1.7299999999999898 26.5 -1.7299999999999898 Q26.5 -1.7299999999999898 26.5 -1.7299999999999898 H93.5 Q93.5 -1.7299999999999898 93.5 -1.7299999999999898 Q93.5 -1.7299999999999898 93.5 -1.7299999999999898 V-55.35499999999999 Z M60.0 -6.72999999999999 Q66.25 -6.72999999999999 70.625 -11.10499999999999 Q75.0 -15.47999999999999 75.0 -21.72999999999999 Q75.0 -27.97999999999999 70.625 -32.35499999999999 Q66.25 -36.72999999999999 60.0 -36.72999999999999 Q53.75 -36.72999999999999 49.375 -32.35499999999999 Q45.0 -27.97999999999999 45.0 -21.72999999999999 Q45.0 -15.47999999999999 49.375 -11.10499999999999 Q53.75 -6.72999999999999 60.0 -6.72999999999999 Z M31.5 -43.72999999999999 H76.5 V-63.72999999999999 H31.5 V-43.72999999999999 Z"/> + </g> + <g id="Semibold-M" transform="matrix(1 0 0 1 1985.97 1126)"> + <path d="M107.40225 -61.21912499999999 V-1.0778749999999881 Q107.40225 4.460250000000009 103.54625 8.31625000000001 Q99.69025 12.17225000000001 94.152125 12.17225000000001 H25.847875 Q20.30975 12.17225000000001 16.45375 8.31625000000001 Q12.59775 4.460250000000009 12.59775 -1.0778749999999881 V-69.38212499999999 Q12.59775 -74.92025 16.45375 -78.77624999999999 Q20.30975 -82.63224999999998 25.847875 -82.63224999999998 H85.989125 L107.40225 -61.21912499999999 Z M94.152125 -55.62674999999999 80.39675 -69.38212499999999 H25.847875 Q25.847875 -69.38212499999999 25.847875 -69.38212499999999 Q25.847875 -69.38212499999999 25.847875 -69.38212499999999 V-1.0778749999999881 Q25.847875 -1.0778749999999881 25.847875 -1.0778749999999881 Q25.847875 -1.0778749999999881 25.847875 -1.0778749999999881 H94.152125 Q94.152125 -1.0778749999999881 94.152125 -1.0778749999999881 Q94.152125 -1.0778749999999881 94.152125 -1.0778749999999881 V-55.62674999999999 Z M60.0 -6.077874999999988 Q66.25 -6.077874999999988 70.625 -10.452874999999992 Q75.0 -14.827874999999992 75.0 -21.07787499999999 Q75.0 -27.32787499999999 70.625 -31.70287499999999 Q66.25 -36.07787499999999 60.0 -36.07787499999999 Q53.75 -36.07787499999999 49.375 -31.70287499999999 Q45.0 -27.32787499999999 45.0 -21.07787499999999 Q45.0 -14.827874999999992 49.375 -10.452874999999992 Q53.75 -6.077874999999988 60.0 -6.077874999999988 Z M30.847875 -44.38212499999999 H75.847875 V-64.38212499999999 H30.847875 V-44.38212499999999 Z"/> + </g> + <g id="Medium-M" transform="matrix(1 0 0 1 1689.91 1126)"> + <path d="M106.016375 -60.648499999999984 V-0.5887499999999903 Q106.016375 4.134125000000012 102.69025 7.460250000000009 Q99.364125 10.78637500000001 94.64125 10.78637500000001 H25.35875 Q20.635875 10.78637500000001 17.30975 7.460250000000009 Q13.983625 4.134125000000012 13.983625 -0.5887499999999903 V-69.87124999999999 Q13.983625 -74.59412499999999 17.30975 -77.92025 Q20.635875 -81.24637499999999 25.35875 -81.24637499999999 H85.4185 L106.016375 -60.648499999999984 Z M94.64125 -55.83049999999999 80.6005 -69.87124999999999 H25.35875 Q25.35875 -69.87124999999999 25.35875 -69.87124999999999 Q25.35875 -69.87124999999999 25.35875 -69.87124999999999 V-0.5887499999999903 Q25.35875 -0.5887499999999903 25.35875 -0.5887499999999903 Q25.35875 -0.5887499999999903 25.35875 -0.5887499999999903 H94.64125 Q94.64125 -0.5887499999999903 94.64125 -0.5887499999999903 Q94.64125 -0.5887499999999903 94.64125 -0.5887499999999903 V-55.83049999999999 Z M60.0 -5.58874999999999 Q66.25 -5.58874999999999 70.625 -9.96374999999999 Q75.0 -14.33874999999999 75.0 -20.58874999999999 Q75.0 -26.83874999999999 70.625 -31.21374999999999 Q66.25 -35.58874999999999 60.0 -35.58874999999999 Q53.75 -35.58874999999999 49.375 -31.21374999999999 Q45.0 -26.83874999999999 45.0 -20.58874999999999 Q45.0 -14.33874999999999 49.375 -9.96374999999999 Q53.75 -5.58874999999999 60.0 -5.58874999999999 Z M30.35875 -44.87124999999999 H75.35875 V-64.87124999999999 H30.35875 V-44.87124999999999 Z"/> + </g> + <g id="Regular-M" transform="matrix(1 0 0 1 1394.06 1126)"> + <path d="M105.0 -60.22999999999999 V-0.22999999999998977 Q105.0 3.8950000000000102 102.0625 6.83250000000001 Q99.125 9.77000000000001 95.0 9.77000000000001 H25.0 Q20.875 9.77000000000001 17.9375 6.83250000000001 Q15.0 3.8950000000000102 15.0 -0.22999999999998977 V-70.22999999999999 Q15.0 -74.35499999999999 17.9375 -77.29249999999999 Q20.875 -80.22999999999999 25.0 -80.22999999999999 H85.0 L105.0 -60.22999999999999 Z M95.0 -55.97999999999999 80.75 -70.22999999999999 H25.0 Q25.0 -70.22999999999999 25.0 -70.22999999999999 Q25.0 -70.22999999999999 25.0 -70.22999999999999 V-0.22999999999998977 Q25.0 -0.22999999999998977 25.0 -0.22999999999998977 Q25.0 -0.22999999999998977 25.0 -0.22999999999998977 H95.0 Q95.0 -0.22999999999998977 95.0 -0.22999999999998977 Q95.0 -0.22999999999998977 95.0 -0.22999999999998977 V-55.97999999999999 Z M60.0 -5.22999999999999 Q66.25 -5.22999999999999 70.625 -9.60499999999999 Q75.0 -13.97999999999999 75.0 -20.22999999999999 Q75.0 -26.47999999999999 70.625 -30.85499999999999 Q66.25 -35.22999999999999 60.0 -35.22999999999999 Q53.75 -35.22999999999999 49.375 -30.85499999999999 Q45.0 -26.47999999999999 45.0 -20.22999999999999 Q45.0 -13.97999999999999 49.375 -9.60499999999999 Q53.75 -5.22999999999999 60.0 -5.22999999999999 Z M30.0 -45.22999999999999 H75.0 V-65.22999999999999 H30.0 V-45.22999999999999 Z"/> + </g> + <g id="Light-M" transform="matrix(1 0 0 1 1098.18 1126)"> + <path d="M102.499875 -59.17224999999999 V-1.7686249999999895 Q102.499875 2.0198750000000096 99.874875 4.64487500000001 Q97.249875 7.26987500000001 93.461375 7.26987500000001 H26.538625 Q22.750125 7.26987500000001 20.125125 4.64487500000001 Q17.500125 2.0198750000000096 17.500125 -1.7686249999999895 V-68.691375 Q17.500125 -72.47987499999999 20.125125 -75.10487499999999 Q22.750125 -77.72987499999999 26.538625 -77.72987499999999 H83.94225 L102.499875 -59.17224999999999 Z M95.0 -55.97999999999999 80.75 -70.22999999999999 H26.538625 Q25.8655 -70.22999999999999 25.43275 -69.79724999999999 Q25.0 -69.36449999999999 25.0 -68.691375 V-1.7686249999999895 Q25.0 -1.0954999999999906 25.43275 -0.6627499999999884 Q25.8655 -0.22999999999998977 26.538625 -0.22999999999998977 H93.461375 Q94.1345 -0.22999999999998977 94.56725 -0.6627499999999884 Q95.0 -1.0954999999999906 95.0 -1.7686249999999895 V-55.97999999999999 Z M60.0 -8.88412499999999 Q65.19225 -8.88412499999999 68.846 -12.537874999999993 Q72.499875 -16.191749999999992 72.499875 -21.38387499999999 Q72.499875 -26.57612499999999 68.846 -30.22999999999999 Q65.19225 -33.88374999999999 60.0 -33.88374999999999 Q54.80775 -33.88374999999999 51.154 -30.22999999999999 Q47.500125 -26.57612499999999 47.500125 -21.38387499999999 Q47.500125 -16.191749999999992 51.154 -12.537874999999993 Q54.80775 -8.88412499999999 60.0 -8.88412499999999 Z M31.92325 -45.80699999999999 H72.980625 V-63.306749999999994 H31.92325 V-45.80699999999999 Z"/> + </g> + <g id="Thin-M" transform="matrix(1 0 0 1 802.565 1126)"> + <path d="M100.0 -58.11462499999999 V-3.306874999999991 Q100.0 0.14500000000001023 97.6875 2.4575000000000102 Q95.375 4.77000000000001 91.923125 4.77000000000001 H28.076875 Q24.625 4.77000000000001 22.3125 2.4575000000000102 Q20.0 0.14500000000001023 20.0 -3.306874999999991 V-67.15312499999999 Q20.0 -70.60499999999999 22.3125 -72.91749999999999 Q24.625 -75.22999999999999 28.076875 -75.22999999999999 H82.884625 L100.0 -58.11462499999999 Z M95.0 -55.97999999999999 80.75 -70.22999999999999 H28.076875 Q26.73075 -70.22999999999999 25.865375 -69.36462499999999 Q25.0 -68.49924999999999 25.0 -67.15312499999999 V-3.306874999999991 Q25.0 -1.9607499999999902 25.865375 -1.09537499999999 Q26.73075 -0.22999999999998977 28.076875 -0.22999999999998977 H91.923125 Q93.26925 -0.22999999999998977 94.134625 -1.09537499999999 Q95.0 -1.9607499999999902 95.0 -3.306874999999991 V-55.97999999999999 Z M60.0 -12.537624999999991 Q64.134625 -12.537624999999991 67.067375 -15.47037499999999 Q70.0 -18.40299999999999 70.0 -22.53762499999999 Q70.0 -26.67224999999999 67.067375 -29.60499999999999 Q64.134625 -32.53774999999999 60.0 -32.53774999999999 Q55.865375 -32.53774999999999 52.932625 -29.60499999999999 Q50.0 -26.67224999999999 50.0 -22.53762499999999 Q50.0 -18.40299999999999 52.932625 -15.47037499999999 Q55.865375 -12.537624999999991 60.0 -12.537624999999991 Z M33.846125 -46.38387499999999 H70.961625 V-61.38387499999999 H33.846125 V-46.38387499999999 Z"/> + </g> + <g id="Ultralight-M" transform="matrix(1 0 0 1 506.415 1126)"> + <path d="M98.5 -57.47999999999999 V-4.22999999999999 Q98.5 -0.9799999999999898 96.375 1.1450000000000102 Q94.25 3.2700000000000102 91.0 3.2700000000000102 H29.0 Q25.75 3.2700000000000102 23.625 1.1450000000000102 Q21.5 -0.9799999999999898 21.5 -4.22999999999999 V-66.22999999999999 Q21.5 -69.47999999999999 23.625 -71.60499999999999 Q25.75 -73.72999999999999 29.0 -73.72999999999999 H82.25 L98.5 -57.47999999999999 Z M95.0 -55.97999999999999 80.75 -70.22999999999999 H29.0 Q27.25 -70.22999999999999 26.125 -69.10499999999999 Q25.0 -67.97999999999999 25.0 -66.22999999999999 V-4.22999999999999 Q25.0 -2.4799999999999898 26.125 -1.3549999999999898 Q27.25 -0.22999999999998977 29.0 -0.22999999999998977 H91.0 Q92.75 -0.22999999999998977 93.875 -1.3549999999999898 Q95.0 -2.4799999999999898 95.0 -4.22999999999999 V-55.97999999999999 Z M60.0 -14.72999999999999 Q63.5 -14.72999999999999 66.0 -17.22999999999999 Q68.5 -19.72999999999999 68.5 -23.22999999999999 Q68.5 -26.72999999999999 66.0 -29.22999999999999 Q63.5 -31.72999999999999 60.0 -31.72999999999999 Q56.5 -31.72999999999999 54.0 -29.22999999999999 Q51.5 -26.72999999999999 51.5 -23.22999999999999 Q51.5 -19.72999999999999 54.0 -17.22999999999999 Q56.5 -14.72999999999999 60.0 -14.72999999999999 Z M35.0 -46.72999999999999 H69.75 V-60.22999999999999 H35.0 V-46.72999999999999 Z"/> + </g> + <g id="Bold-S" transform="matrix(1 0 0 1 2294.23 696)"> + <path d="M83.83125 -55.54674999999999 V-10.376499999999986 Q83.83125 -5.474837499999989 80.46320625 -2.106793749999987 Q77.09516250000001 1.2612500000000129 72.1935 1.2612500000000129 H22.4865 Q17.584837500000003 1.2612500000000129 14.21679375 -2.106793749999987 Q10.84875 -5.474837499999989 10.84875 -10.376499999999986 V-60.08349999999999 Q10.84875 -64.9851625 14.21679375 -68.35320624999999 Q17.584837500000003 -71.72124999999998 22.4865 -71.72124999999998 H67.65675 L83.83125 -55.54674999999999 Z M72.1935 -50.71412499999999 62.824125 -60.08349999999999 H22.4865 Q22.4865 -60.08349999999999 22.4865 -60.08349999999999 Q22.4865 -60.08349999999999 22.4865 -60.08349999999999 V-10.376499999999986 Q22.4865 -10.376499999999986 22.4865 -10.376499999999986 Q22.4865 -10.376499999999986 22.4865 -10.376499999999986 H72.1935 Q72.1935 -10.376499999999986 72.1935 -10.376499999999986 Q72.1935 -10.376499999999986 72.1935 -10.376499999999986 V-50.71412499999999 Z M47.34 -13.926999999999989 Q51.778125 -13.926999999999989 54.8848125 -17.03368749999999 Q57.9915 -20.140374999999985 57.9915 -24.57849999999999 Q57.9915 -29.01662499999999 54.8848125 -32.12331249999999 Q51.778125 -35.22999999999999 47.34 -35.22999999999999 Q42.901875000000004 -35.22999999999999 39.795187500000004 -32.12331249999999 Q36.688500000000005 -29.01662499999999 36.688500000000005 -24.57849999999999 Q36.688500000000005 -20.140374999999985 39.795187500000004 -17.03368749999999 Q42.901875000000004 -13.926999999999989 47.34 -13.926999999999989 Z M27.2205 -41.14749999999999 H60.3585 V-55.34949999999999 H27.2205 V-41.14749999999999 Z"/> + </g> + <g id="Semibold-S" transform="matrix(1 0 0 1 1998.1 696)"> + <path d="M82.37337525 -54.94641962499999 V-9.861973374999987 Q82.37337525 -5.817855249999987 79.56276000000001 -3.007239999999987 Q76.75214475 -0.19662474999998736 72.708026625 -0.19662474999998736 H21.971973375 Q17.92785525 -0.19662474999998736 15.11724 -3.007239999999987 Q12.306624750000001 -5.817855249999987 12.306624750000001 -9.861973374999987 V-60.59802662499999 Q12.306624750000001 -64.64214474999999 15.11724 -67.45276 Q17.92785525 -70.26337524999998 21.971973375 -70.26337524999998 H67.056419625 L82.37337525 -54.94641962499999 Z M72.708026625 -50.92853574999999 63.03853575 -60.59802662499999 H21.971973375 Q21.971973375 -60.59802662499999 21.971973375 -60.59802662499999 Q21.971973375 -60.59802662499999 21.971973375 -60.59802662499999 V-9.861973374999987 Q21.971973375 -9.861973374999987 21.971973375 -9.861973374999987 Q21.971973375 -9.861973374999987 21.971973375 -9.861973374999987 H72.708026625 Q72.708026625 -9.861973374999987 72.708026625 -9.861973374999987 Q72.708026625 -9.861973374999987 72.708026625 -9.861973374999987 V-50.92853574999999 Z M47.34 -13.41247337499999 Q51.778125 -13.41247337499999 54.8848125 -16.51916087499999 Q57.9915 -19.62584837499999 57.9915 -24.06397337499999 Q57.9915 -28.50209837499999 54.8848125 -31.608785874999988 Q51.778125 -34.71547337499999 47.34 -34.71547337499999 Q42.901875000000004 -34.71547337499999 39.795187500000004 -31.608785874999988 Q36.688500000000005 -28.50209837499999 36.688500000000005 -24.06397337499999 Q36.688500000000005 -19.62584837499999 39.795187500000004 -16.51916087499999 Q42.901875000000004 -13.41247337499999 47.34 -13.41247337499999 Z M26.705973375000003 -41.66202662499999 H59.843973375000004 V-55.864026624999994 H26.705973375000003 V-41.66202662499999 Z"/> + </g> + <g id="Medium-S" transform="matrix(1 0 0 1 1701.85 696)"> + <path d="M81.279919875 -54.49619649999998 V-9.476053749999988 Q81.279919875 -6.075167874999988 78.887376 -3.6826239999999864 Q76.494832125 -1.290080124999987 73.09394625 -1.290080124999987 H21.58605375 Q18.185167875 -1.290080124999987 15.792624 -3.6826239999999864 Q13.400080125 -6.075167874999988 13.400080125 -9.476053749999988 V-60.98394624999999 Q13.400080125 -64.38483212499999 15.792624 -66.77737599999999 Q18.185167875 -69.16991987499999 21.58605375 -69.16991987499999 H66.6061965 L81.279919875 -54.49619649999998 Z M73.09394625 -51.08929449999999 63.1992945 -60.98394624999999 H21.58605375 Q21.58605375 -60.98394624999999 21.58605375 -60.98394624999999 Q21.58605375 -60.98394624999999 21.58605375 -60.98394624999999 V-9.476053749999988 Q21.58605375 -9.476053749999988 21.58605375 -9.476053749999988 Q21.58605375 -9.476053749999988 21.58605375 -9.476053749999988 H73.09394625 Q73.09394625 -9.476053749999988 73.09394625 -9.476053749999988 Q73.09394625 -9.476053749999988 73.09394625 -9.476053749999988 V-51.08929449999999 Z M47.34 -13.026553749999987 Q51.778125 -13.026553749999987 54.8848125 -16.133241249999987 Q57.9915 -19.239928749999986 57.9915 -23.67805374999999 Q57.9915 -28.11617874999999 54.8848125 -31.22286624999999 Q51.778125 -34.32955374999999 47.34 -34.32955374999999 Q42.901875000000004 -34.32955374999999 39.795187500000004 -31.22286624999999 Q36.688500000000005 -28.11617874999999 36.688500000000005 -23.67805374999999 Q36.688500000000005 -19.239928749999986 39.795187500000004 -16.133241249999987 Q42.901875000000004 -13.026553749999987 47.34 -13.026553749999987 Z M26.320053750000003 -42.04794624999999 H59.458053750000005 V-56.249946249999994 H26.320053750000003 V-42.04794624999999 Z"/> + </g> + <g id="Regular-S" transform="matrix(1 0 0 1 1405.73 696)"> + <path d="M80.47800000000001 -54.16599999999998 V-9.192999999999987 Q80.47800000000001 -6.263837499999987 78.39208125 -4.177918749999989 Q76.30616250000001 -2.0919999999999863 73.37700000000001 -2.0919999999999863 H21.303 Q18.3738375 -2.0919999999999863 16.287918750000003 -4.177918749999989 Q14.202 -6.263837499999987 14.202 -9.192999999999987 V-61.266999999999996 Q14.202 -64.1961625 16.287918750000003 -66.28208124999999 Q18.3738375 -68.368 21.303 -68.368 H66.276 L80.47800000000001 -54.16599999999998 Z M73.37700000000001 -51.20724999999999 63.31725 -61.266999999999996 H21.303 Q21.303 -61.266999999999996 21.303 -61.266999999999996 Q21.303 -61.266999999999996 21.303 -61.266999999999996 V-9.192999999999987 Q21.303 -9.192999999999987 21.303 -9.192999999999987 Q21.303 -9.192999999999987 21.303 -9.192999999999987 H73.37700000000001 Q73.37700000000001 -9.192999999999987 73.37700000000001 -9.192999999999987 Q73.37700000000001 -9.192999999999987 73.37700000000001 -9.192999999999987 V-51.20724999999999 Z M47.34 -12.743499999999987 Q51.778125 -12.743499999999987 54.8848125 -15.850187499999986 Q57.9915 -18.95687499999999 57.9915 -23.39499999999999 Q57.9915 -27.83312499999999 54.8848125 -30.939812499999988 Q51.778125 -34.04649999999999 47.34 -34.04649999999999 Q42.901875000000004 -34.04649999999999 39.795187500000004 -30.939812499999988 Q36.688500000000005 -27.83312499999999 36.688500000000005 -23.39499999999999 Q36.688500000000005 -18.95687499999999 39.795187500000004 -15.850187499999986 Q42.901875000000004 -12.743499999999987 47.34 -12.743499999999987 Z M26.037000000000003 -42.33099999999999 H59.175000000000004 V-56.53299999999999 H26.037000000000003 V-42.33099999999999 Z"/> + </g> + <g id="Light-S" transform="matrix(1 0 0 1 1109.75 696)"> + <path d="M78.505401375 -53.331435249999984 V-10.406975124999988 Q78.505401375 -7.7433111249999875 76.66604512500001 -5.9039548749999895 Q74.826688875 -4.064598624999988 72.163024875 -4.064598624999988 H22.516975125000002 Q19.853311125 -4.064598624999988 18.013954875000003 -5.9039548749999895 Q16.174598625 -7.7433111249999875 16.174598625 -10.406975124999988 V-60.05302487499999 Q16.174598625 -62.71668887499999 18.013954875000003 -64.556045125 Q19.853311125 -66.39540137499999 22.516975125000002 -66.39540137499999 H65.44143525 L78.505401375 -53.331435249999984 Z M73.37700000000001 -51.20724999999999 63.31725 -61.266999999999996 H22.516975125000002 Q21.985879500000003 -61.266999999999996 21.64443975 -60.92556024999999 Q21.303 -60.5841205 21.303 -60.05302487499999 V-10.406975124999988 Q21.303 -9.87587949999999 21.64443975 -9.534439749999986 Q21.985879500000003 -9.192999999999987 22.516975125000002 -9.192999999999987 H72.163024875 Q72.69412050000001 -9.192999999999987 73.03556025 -9.534439749999986 Q73.37700000000001 -9.87587949999999 73.37700000000001 -10.406975124999988 V-51.20724999999999 Z M47.34 -15.626604624999988 Q50.943560250000004 -15.626604624999988 53.481181500000005 -18.16422587499999 Q56.018901375000006 -20.701945749999986 56.018901375000006 -24.305407374999987 Q56.018901375000006 -27.90896762499999 53.481181500000005 -30.44668749999999 Q50.943560250000004 -32.98430874999999 47.34 -32.98430874999999 Q43.73643975 -32.98430874999999 41.1988185 -30.44668749999999 Q38.661098625 -27.90896762499999 38.661098625 -24.305407374999987 Q38.661098625 -20.701945749999986 41.1988185 -18.16422587499999 Q43.73643975 -15.626604624999988 47.34 -15.626604624999988 Z M27.554444250000003 -42.78625299999999 H57.58171312500001 V-55.01555574999999 H27.554444250000003 V-42.78625299999999 Z"/> + </g> + <g id="Thin-S" transform="matrix(1 0 0 1 814.015 696)"> + <path d="M76.533 -52.49696912499999 V-11.620654374999987 Q76.533 -9.222587499999989 74.94020625 -7.629793749999987 Q73.3474125 -6.036999999999988 70.949345625 -6.036999999999988 H23.730654375 Q21.332587500000002 -6.036999999999988 19.73979375 -7.629793749999987 Q18.147000000000002 -9.222587499999989 18.147000000000002 -11.620654374999987 V-58.83934562499999 Q18.147000000000002 -61.23741249999999 19.73979375 -62.83020624999999 Q21.332587500000002 -64.42299999999999 23.730654375 -64.42299999999999 H64.606969125 L76.533 -52.49696912499999 Z M73.37700000000001 -51.20724999999999 63.31725 -61.266999999999996 H23.730654375 Q22.668561750000002 -61.266999999999996 21.985780875 -60.58421912499999 Q21.303 -59.901438249999984 21.303 -58.83934562499999 V-11.620654374999987 Q21.303 -10.558561749999988 21.985780875 -9.875780874999986 Q22.668561750000002 -9.192999999999987 23.730654375 -9.192999999999987 H70.949345625 Q72.01143825 -9.192999999999987 72.694219125 -9.875780874999986 Q73.37700000000001 -10.558561749999988 73.37700000000001 -11.620654374999987 V-51.20724999999999 Z M47.34 -18.509216124999988 Q50.109094125 -18.509216124999988 52.077846375 -20.47796837499999 Q54.0465 -22.44662199999999 54.0465 -25.215716124999986 Q54.0465 -27.98481024999999 52.077846375 -29.95356249999999 Q50.109094125 -31.922314749999984 47.34 -31.922314749999984 Q44.570905875 -31.922314749999984 42.602153625 -29.95356249999999 Q40.633500000000005 -27.98481024999999 40.633500000000005 -25.215716124999986 Q40.633500000000005 -22.44662199999999 42.602153625 -20.47796837499999 Q44.570905875 -18.509216124999988 47.34 -18.509216124999988 Z M29.071592625 -43.24140737499999 H55.988722125 V-53.498407374999985 H29.071592625 V-43.24140737499999 Z"/> + </g> + <g id="Ultralight-S" transform="matrix(1 0 0 1 517.792 696)"> + <path d="M75.3495 -51.99624999999999 V-12.348999999999986 Q75.3495 -10.110212499999989 73.90464375 -8.665356249999988 Q72.4597875 -7.220499999999987 70.221 -7.220499999999987 H24.459 Q22.220212500000002 -7.220499999999987 20.77535625 -8.665356249999988 Q19.3305 -10.110212499999989 19.3305 -12.348999999999986 V-58.11099999999999 Q19.3305 -60.34978749999999 20.77535625 -61.79464374999999 Q22.220212500000002 -63.23949999999999 24.459 -63.23949999999999 H64.10625 L75.3495 -51.99624999999999 Z M73.37700000000001 -51.20724999999999 63.31725 -61.266999999999996 H24.459 Q23.07825 -61.266999999999996 22.190625 -60.379374999999996 Q21.303 -59.491749999999996 21.303 -58.11099999999999 V-12.348999999999986 Q21.303 -10.968249999999987 22.190625 -10.080624999999987 Q23.07825 -9.192999999999987 24.459 -9.192999999999987 H70.221 Q71.60175000000001 -9.192999999999987 72.48937500000001 -10.080624999999987 Q73.37700000000001 -10.968249999999987 73.37700000000001 -12.348999999999986 V-51.20724999999999 Z M47.34 -20.23899999999999 Q49.608375 -20.23899999999999 51.235687500000004 -21.866312499999985 Q52.863 -23.493624999999987 52.863 -25.761999999999986 Q52.863 -28.030374999999985 51.235687500000004 -29.657687499999987 Q49.608375 -31.28499999999999 47.34 -31.28499999999999 Q45.071625000000004 -31.28499999999999 43.4443125 -29.657687499999987 Q41.817 -28.030374999999985 41.817 -25.761999999999986 Q41.817 -23.493624999999987 43.4443125 -21.866312499999985 Q45.071625000000004 -20.23899999999999 47.34 -20.23899999999999 Z M29.982000000000003 -43.51449999999999 H55.03275 V-52.587999999999994 H29.982000000000003 V-43.51449999999999 Z"/> + </g> + </g> +</svg> \ No newline at end of file diff --git a/ApplicationLibrary/Service/ProfileUpdateTask.swift b/ApplicationLibrary/Service/ProfileUpdateTask.swift new file mode 100644 index 0000000000000000000000000000000000000000..65ce3d76e5747bd3a711a29c247dcf39d4e05706 --- /dev/null +++ b/ApplicationLibrary/Service/ProfileUpdateTask.swift @@ -0,0 +1,76 @@ +import Foundation +import Library + +public enum ProfileUpdateTask { + static let minUpdateInterval: TimeInterval = 15 * 60 + static let defaultUpdateInterval: TimeInterval = 60 * 60 + + private static var timer: Timer? + + public static func configure() async throws { + timer?.invalidate() + timer = nil + let profiles = try await ProfileManager.listAutoUpdateEnabled() + if profiles.isEmpty { + return + } + var updateInterval = profiles.map { it in + it.autoUpdateIntervalOrDefault + }.min()! + if updateInterval < minUpdateInterval { + updateInterval = minUpdateInterval + } + timer = Timer(fire: calculateEarliestBeginDate(profiles), interval: updateInterval, repeats: true) { _ in + Task { + await getAndupdateProfiles() + } + } + } + + static func calculateEarliestBeginDate(_ profiles: [Profile]) -> Date { + let nowTime = Date.now + var earliestBeginDate = profiles.map { it in + it.lastUpdated!.addingTimeInterval(it.autoUpdateIntervalOrDefault) + }.min()! + if earliestBeginDate <= nowTime { + earliestBeginDate = nowTime + } + return earliestBeginDate + } + + private nonisolated static func getAndupdateProfiles() async { + do { + _ = try await updateProfiles(ProfileManager.listAutoUpdateEnabled()) + NSLog("profile update task succeed") + } catch { + NSLog("profile update task failed: \(error.localizedDescription)") + } + } + + static func updateProfiles(_ profiles: [Profile]) async -> Bool { + var success = true + for profile in profiles { + if profile.lastUpdated! > Date(timeIntervalSinceNow: -profile.autoUpdateIntervalOrDefault) { + continue + } + do { + try await profile.updateRemoteProfile() + NSLog("Updated profile \(profile.name)") + } catch { + NSLog("Update profile \(profile.name) failed: \(error.localizedDescription)") + success = false + } + } + return success + } +} + +extension Profile { + var autoUpdateIntervalOrDefault: TimeInterval { + if autoUpdateInterval > 0 { + return TimeInterval(autoUpdateInterval * 60) + } else { + return ProfileUpdateTask.defaultUpdateInterval + } + } +} diff --git a/ApplicationLibrary/Service/UIProfileUpdateTask.swift b/ApplicationLibrary/Service/UIProfileUpdateTask.swift new file mode 100644 index 0000000000000000000000000000000000000000..4fde2a727d908c28eb3cc10526897a1777511162 --- /dev/null +++ b/ApplicationLibrary/Service/UIProfileUpdateTask.swift @@ -0,0 +1,79 @@ +import BackgroundTasks +import Foundation +import Library +#if canImport(UIKit) + import UIKit +#endif + +#if os(iOS) || os(tvOS) + public class UIProfileUpdateTask: BGAppRefreshTask { + private static let taskSchedulerPermittedIdentifier = "\(FilePath.packageName).update_profiles" + + private static var registered = false + public static func configure() throws { + if !registered { + let success = BGTaskScheduler.shared.register(forTaskWithIdentifier: taskSchedulerPermittedIdentifier, using: nil) { task in + NSLog("profile update task started") + Task { + await UIProfileUpdateTask.getAndUpdateProfiles(task) + } + } + if !success { + throw NSError(domain: "register task failed", code: 0) + } + registered = true + } + Task { + BGTaskScheduler.shared.cancelAllTaskRequests() + let profiles = try await ProfileManager.listAutoUpdateEnabled() + if profiles.isEmpty { + return + } + try scheduleUpdate(ProfileUpdateTask.calculateEarliestBeginDate(profiles)) + } + Task { + if await UIApplication.shared.backgroundRefreshStatus != .available { + await updateOnce() + } + } + } + + private nonisolated static func updateOnce() async { + NSLog("update profiles at start since background refresh unavailable") + let profiles: [Profile] + do { + profiles = try await ProfileManager.listAutoUpdateEnabled() + } catch { + return + } + if profiles.isEmpty { + return + } + _ = await ProfileUpdateTask.updateProfiles(profiles) + } + + private nonisolated static func getAndUpdateProfiles(_ task: BGTask) async { + let profiles: [Profile] + do { + profiles = try await ProfileManager.listAutoUpdateEnabled() + } catch { + return + } + if profiles.isEmpty { + return + } + let success = await ProfileUpdateTask.updateProfiles(profiles) + try? scheduleUpdate(ProfileUpdateTask.calculateEarliestBeginDate(profiles)) + task.setTaskCompleted(success: success) + task.expirationHandler = { + try? scheduleUpdate(nil) + } + } + + private static func scheduleUpdate(_ earliestBeginDate: Date?) throws { + let request = BGAppRefreshTaskRequest(identifier: taskSchedulerPermittedIdentifier) + request.earliestBeginDate = earliestBeginDate + try BGTaskScheduler.shared.submit(request) + } + } +#endif diff --git a/ApplicationLibrary/Views/Abstract/Alert.swift b/ApplicationLibrary/Views/Abstract/Alert.swift new file mode 100644 index 0000000000000000000000000000000000000000..02a4bd9dc087180705243091f558e9b291bf2709 --- /dev/null +++ b/ApplicationLibrary/Views/Abstract/Alert.swift @@ -0,0 +1,47 @@ +import Foundation +import SwiftUI + +public extension Alert { + init(_ error: Error, _ dismissAction: (() -> Void)? = nil) { + self.init( + errorMessage: error.localizedDescription, + dismissAction + ) + } + + init(errorMessage: String, _ dismissAction: (() -> Void)? = nil) { + self.init( + title: Text("Error"), + message: Text(errorMessage), + dismissButton: .default(Text("Ok")) { + dismissAction?() + } + ) + } +} + +public extension View { + func alertBinding(_ binding: Binding<Alert?>) -> some View { + alert(isPresented: Binding(get: { + binding.wrappedValue != nil + }, set: { newValue, _ in + if !newValue { + binding.wrappedValue = nil + } + })) { + binding.wrappedValue! + } + } + + func alertBinding(_ binding: Binding<Alert?>, _ isLoading: Binding<Bool>) -> some View { + alert(isPresented: Binding(get: { + binding.wrappedValue != nil + }, set: { newValue, _ in + if !newValue, !isLoading.wrappedValue { + binding.wrappedValue = nil + } + })) { + binding.wrappedValue! + } + } +} diff --git a/ApplicationLibrary/Views/Abstract/BackButton.swift b/ApplicationLibrary/Views/Abstract/BackButton.swift new file mode 100644 index 0000000000000000000000000000000000000000..afcbdeb9e21a1afa0c38986f83a847c3d751a14c --- /dev/null +++ b/ApplicationLibrary/Views/Abstract/BackButton.swift @@ -0,0 +1,14 @@ +import Foundation +import SwiftUI + +public struct BackButton: View { + @Environment(\.dismiss) private var dismiss + + public var body: some View { + Button { + dismiss() + } label: { + Image(systemName: "chevron.backward") + } + } +} diff --git a/ApplicationLibrary/Views/Abstract/Binding+Setter.swift b/ApplicationLibrary/Views/Abstract/Binding+Setter.swift new file mode 100644 index 0000000000000000000000000000000000000000..395f4fe049a1a91ca2402de36ec749f135a48c31 --- /dev/null +++ b/ApplicationLibrary/Views/Abstract/Binding+Setter.swift @@ -0,0 +1,12 @@ +import Foundation +import SwiftUI + +public extension Binding { + func withSetter(_ setter: @escaping (Value) -> Void) -> Binding<Value> { + Binding { + wrappedValue + } set: { [setter] newValue, _ in + setter(newValue) + } + } +} diff --git a/ApplicationLibrary/Views/Abstract/Binding+Unwrap.swift b/ApplicationLibrary/Views/Abstract/Binding+Unwrap.swift new file mode 100644 index 0000000000000000000000000000000000000000..25bf0fd2f86fd695665455c3b51fb263cf46c78c --- /dev/null +++ b/ApplicationLibrary/Views/Abstract/Binding+Unwrap.swift @@ -0,0 +1,29 @@ +import SwiftUI + +public extension Binding { + func unwrapped<T>(_ defaultValue: T) -> Binding<T> where Value == T? { + Binding<T>(get: { + wrappedValue ?? defaultValue + }, set: { newValue in + wrappedValue = newValue + }) + } +} + +public extension Binding where Value == Int32 { + func stringBinding(defaultValue: Int32) -> Binding<String> { + Binding<String> { + var intValue = wrappedValue + if intValue == 0 { + intValue = defaultValue + } + return String(intValue) + } set: { newValue in + var newIntValue = Int32(newValue) ?? defaultValue + if newIntValue == 0 { + newIntValue = defaultValue + } + wrappedValue = newIntValue + } + } +} diff --git a/ApplicationLibrary/Views/Abstract/DeleteButton.swift b/ApplicationLibrary/Views/Abstract/DeleteButton.swift new file mode 100644 index 0000000000000000000000000000000000000000..746f8c1f890720ee97d5f59a5c55644f22a51325 --- /dev/null +++ b/ApplicationLibrary/Views/Abstract/DeleteButton.swift @@ -0,0 +1,46 @@ +import Foundation +import SwiftUI + +public struct DeleteButton<Label>: View where Label: View { + private let action: () async -> Void + private let label: Label + + @State private var performDelete = false + @State private var timer: Timer? + @State private var isLoading = false + + public init(action: @escaping () async -> Void, @ViewBuilder label: () -> Label) { + self.action = action + self.label = label() + } + + public var body: some View { + Button(role: .destructive) { + isLoading = true + if let timer { + timer.invalidate() + } + if !performDelete { + performDelete = true + timer = Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { _ in + timer = nil + performDelete = false + } + isLoading = true + } else { + Task { + await action() + isLoading = false + performDelete = false + } + } + } label: { + if !performDelete { + label + } else { + label.foregroundStyle(.red) + } + } + .disabled(isLoading) + } +} diff --git a/ApplicationLibrary/Views/Abstract/DeviceCensorship.swift b/ApplicationLibrary/Views/Abstract/DeviceCensorship.swift new file mode 100644 index 0000000000000000000000000000000000000000..9d7993ad9a6d0ab31f39fc704776021783d4d5ed --- /dev/null +++ b/ApplicationLibrary/Views/Abstract/DeviceCensorship.swift @@ -0,0 +1,80 @@ +import Foundation +#if canImport(UIKit) + import UIKit +#elseif canImport(AppKit) + import AppKit +#endif + +public enum DeviceCensorship { + public static func isChinaDevice() -> Bool { + let bannedCharacter = "\u{1F1F9}\u{1F1FC}" as NSString + var imageData: Data + #if canImport(UIKit) + let attributes = [NSAttributedString.Key.font: + UIFont.systemFont(ofSize: 8)] + UIGraphicsBeginImageContext(bannedCharacter.size(withAttributes: attributes)) + bannedCharacter.draw(at: CGPoint(x: 0, y: 0), withAttributes: attributes) + var imagePNG: Data? + if let charImage = UIGraphicsGetImageFromCurrentImageContext() { + imagePNG = charImage.pngData() + } + UIGraphicsEndImageContext() + guard let imagePNG else { + return false + } + guard let uiImage = UIImage(data: imagePNG) else { + return false + } + guard let cgImage = uiImage.cgImage else { return false } + guard let cgImageData = cgImage.dataProvider?.data as Data? else { return false } + imageData = cgImageData + #elseif canImport(AppKit) + let attributes = [NSAttributedString.Key.font: + NSFont.systemFont(ofSize: 8)] + let characterSize = bannedCharacter.size(withAttributes: attributes) + let characterRect = NSRect(origin: .zero, size: characterSize) + + let characterBitmap = NSBitmapImageRep(bitmapDataPlanes: nil, + pixelsWide: Int(characterSize.width), + pixelsHigh: Int(characterSize.height), + bitsPerSample: 8, + samplesPerPixel: 4, + hasAlpha: true, + isPlanar: false, + colorSpaceName: NSColorSpaceName.calibratedRGB, + bytesPerRow: 0, + bitsPerPixel: 0) + + NSGraphicsContext.saveGraphicsState() + NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: characterBitmap!) + NSGraphicsContext.current?.imageInterpolation = .high + + bannedCharacter.draw(in: characterRect, withAttributes: attributes) + + NSGraphicsContext.restoreGraphicsState() + + guard let imagePNG = characterBitmap?.representation(using: .png, properties: [:]) else { + return false + } + + guard let nsImage = NSImage(data: imagePNG) else { + return false + } + guard let cgImage = nsImage.cgImage(forProposedRect: nil, context: nil, hints: nil) else { + return false + } + guard let cgImageData = cgImage.dataProvider?.data as Data? else { return false } + imageData = cgImageData + #endif + let rawData: UnsafePointer<UInt8> = CFDataGetBytePtr(imageData as CFData) + for index in stride(from: 0, to: imageData.count, by: 4) { + let r = rawData[index] + let g = rawData[index + 1] + let b = rawData[index + 2] + if !(r == g && g == b) { + return false + } + } + return true + } +} diff --git a/ApplicationLibrary/Views/Abstract/FormItem.swift b/ApplicationLibrary/Views/Abstract/FormItem.swift new file mode 100644 index 0000000000000000000000000000000000000000..b5a3c3616d2d17df6a90519df0db19f50242a496 --- /dev/null +++ b/ApplicationLibrary/Views/Abstract/FormItem.swift @@ -0,0 +1,100 @@ +import Foundation +import SwiftUI + +public func FormView(@ViewBuilder content: () -> some View) -> some View { + Form { + content() + } + #if os(macOS) + .formStyle(.grouped) + #endif +} + +public func FormTextItem(_ name: LocalizedStringKey, _ value: String) -> some View { + HStack { + Text(name) + Spacer() + Text(value) + .multilineTextAlignment(.trailing) + .font(Font.system(.caption, design: .monospaced)) + #if os(iOS) || os(macOS) + .textSelection(.enabled) + #endif + } +} + +public func FormTextItem(_ name: LocalizedStringKey, _ systemImage: String, @ViewBuilder _ value: () -> some View) -> some View { + HStack { + Label(name, systemImage: systemImage) + Spacer() + value() + .multilineTextAlignment(.trailing) + .font(Font.system(.caption, design: .monospaced)) + #if os(iOS) || os(macOS) + .textSelection(.enabled) + #endif + } +} + +public func FormItem(_ title: String, @ViewBuilder content: () -> some View) -> some View { + #if os(iOS) || os(tvOS) + HStack { + Text(title) + .lineLimit(1) + .layoutPriority(1) + Spacer() + Spacer() + content() + } + #elseif os(macOS) + content() + #endif +} + +public func FormSection(@ViewBuilder content: () -> some View, @ViewBuilder footer: () -> some View) -> some View { + Section { + content() + } footer: { + footer() + .frame(maxWidth: .infinity, alignment: .leading) + } +} + +public func FormButton(action: @escaping () -> Void, @ViewBuilder label: () -> some View) -> some View { + Button(action: action, label: label) + #if os(macOS) + .buttonStyle(.plain) + .foregroundColor(.accentColor) + #endif +} + +public func FormButton(_ titleKey: some StringProtocol, action: @escaping () -> Void) -> some View { + Button(titleKey, action: action) + #if os(macOS) + .buttonStyle(.plain) + .foregroundColor(.accentColor) + #endif +} + +public func FormButton(role: ButtonRole?, action: @escaping () -> Void, @ViewBuilder label: () -> some View) -> some View { + Button(role: role, action: action, label: label) + #if os(macOS) + .buttonStyle(.plain) + .foregroundColor(.accentColor) + #endif +} + +public func FormNavigationLink(@ViewBuilder destination: () -> some View, @ViewBuilder label: () -> some View) -> some View { + #if !os(tvOS) + return NavigationLink(destination: destination, label: label) + #else + return NavigationLink(destination: { + destination() + .toolbar { + ToolbarItemGroup(placement: .topBarLeading) { + BackButton() + } + } + }, label: label) + #endif +} diff --git a/ApplicationLibrary/Views/Abstract/NavigationDestinationCompat.swift b/ApplicationLibrary/Views/Abstract/NavigationDestinationCompat.swift new file mode 100644 index 0000000000000000000000000000000000000000..9df4b21370330802886317931e1b0919b60575d0 --- /dev/null +++ b/ApplicationLibrary/Views/Abstract/NavigationDestinationCompat.swift @@ -0,0 +1,11 @@ +import SwiftUI + +func NavigationDestinationCompat(isPresented: Binding<Bool>, @ViewBuilder destination: () -> some View) -> some View { + NavigationLink( + destination: destination(), + isActive: isPresented, + label: { + EmptyView() + } + ) +} diff --git a/ApplicationLibrary/Views/Abstract/NavigationStackCompat.swift b/ApplicationLibrary/Views/Abstract/NavigationStackCompat.swift new file mode 100644 index 0000000000000000000000000000000000000000..3822d5bf90416a796020919f73b84a8ed65c8411 --- /dev/null +++ b/ApplicationLibrary/Views/Abstract/NavigationStackCompat.swift @@ -0,0 +1,17 @@ +import SwiftUI + +public func NavigationStackCompat(@ViewBuilder content: () -> some View) -> some View { + viewBuilder { + if #available(iOS 17.0, *) { + // view not updating in iOS 16, but why? + NavigationStack { + content() + } + } else { + NavigationView(content: content) + #if !os(macOS) + .navigationViewStyle(.stack) + #endif + } + } +} diff --git a/ApplicationLibrary/Views/Abstract/RequestReviewButton.swift b/ApplicationLibrary/Views/Abstract/RequestReviewButton.swift new file mode 100644 index 0000000000000000000000000000000000000000..a7459cd180b01e9ad38d615d6f0057f377c4de4e --- /dev/null +++ b/ApplicationLibrary/Views/Abstract/RequestReviewButton.swift @@ -0,0 +1,49 @@ +#if !os(tvOS) + + import StoreKit + import SwiftUI + + public func RequestReviewButton(label: @escaping () -> some View) -> some View { + viewBuilder { + if #available(iOS 16.0, macOS 13.0, visionOS 1.0, *) { + RequestReviewButton0(label: label) + } else { + #if os(iOS) + RequestReviewButton1(label: label) + #else + EmptyView() + #endif + } + } + } + + @available(iOS 16.0, macOS 13.0, visionOS 1.0, *) + struct RequestReviewButton0<Label: View>: View { + @Environment(\.requestReview) private var requestReview + + private let label: () -> Label + init(label: @escaping () -> Label) { + self.label = label + } + + var body: some View { + FormButton(action: { + requestReview() + }, label: label) + } + } + + struct RequestReviewButton1<Label: View>: View { + private let label: () -> Label + init(label: @escaping () -> Label) { + self.label = label + } + + var body: some View { + Button(action: { + SKStoreReviewController.requestReview() + }, label: label) + } + } + +#endif diff --git a/ApplicationLibrary/Views/Abstract/ShareButton.swift b/ApplicationLibrary/Views/Abstract/ShareButton.swift new file mode 100644 index 0000000000000000000000000000000000000000..c54a3736f2d7b622eb1a02f09171b830257374c7 --- /dev/null +++ b/ApplicationLibrary/Views/Abstract/ShareButton.swift @@ -0,0 +1,147 @@ +import Foundation +import Library +import SwiftUI +#if canImport(UIKit) + import UIKit +#elseif canImport(AppKit) + import AppKit +#endif + +@MainActor +public struct ProfileShareButton<Label>: View where Label: View { + private let alert: Binding<Alert?> + private let profile: Profile + private let label: () -> Label + + public init(_ alert: Binding<Alert?>, _ profile: Profile, label: @escaping () -> Label) { + self.alert = alert + self.profile = profile + self.label = label + } + + public var body: some View { + #if os(iOS) + if #available(iOS 17.4, *) { + bodyCompat + } else if #available(iOS 16.0, *) { + ShareLink(item: profile, subject: Text(profile.name), preview: SharePreview("Share profile"), label: label) + } else if UIDevice.current.userInterfaceIdiom != .pad { + bodyCompat + } + #else + bodyCompat + #endif + } + + private var bodyCompat: some View { + ShareButtonCompat(alert, label: label) { + try profile.toContent().generateShareFile() + } + } +} + +public struct ShareButtonCompat<Label>: View where Label: View { + private let label: () -> Label + private let itemURL: () throws -> URL + + @Binding private var alert: Alert? + + #if os(macOS) + @State private var sharePresented = false + #endif + + public init(_ alert: Binding<Alert?>, @ViewBuilder label: @escaping () -> Label, itemURL: @escaping () throws -> URL) { + _alert = alert + self.label = label + self.itemURL = itemURL + } + + public var body: some View { + Button(action: shareItem, label: label) + #if os(macOS) + .background(SharingServicePicker($sharePresented, $alert, itemURL)) + #endif + } + + private func shareItem() { + #if os(iOS) + Task { + await shareItem0() + } + #elseif os(macOS) + sharePresented = true + #endif + } + + #if os(iOS) + private nonisolated func shareItem0() async { + do { + let shareItem = try itemURL() + await MainActor.run { + shareItem1(shareItem) + } + } catch { + await MainActor.run { + alert = Alert(error) + } + } + } + + private func shareItem1(_ item: URL) { + if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene { + windowScene.keyWindow?.rootViewController?.present(UIActivityViewController(activityItems: [item], applicationActivities: nil), animated: true, completion: nil) + } + } + #endif +} + +#if os(macOS) + private struct SharingServicePicker: NSViewRepresentable { + @Binding private var isPresented: Bool + @Binding private var alert: Alert? + private let item: () throws -> URL + + init(_ isPresented: Binding<Bool>, _ alert: Binding<Alert?>, _ item: @escaping () throws -> URL) { + _isPresented = isPresented + _alert = alert + self.item = item + } + + func makeNSView(context _: Context) -> NSView { + let view = NSView() + return view + } + + func updateNSView(_ nsView: NSView, context: Context) { + if isPresented { + do { + let picker = try NSSharingServicePicker(items: [item()]) + picker.delegate = context.coordinator + DispatchQueue.main.async { + picker.show(relativeTo: .zero, of: nsView, preferredEdge: .minY) + } + } catch { + alert = Alert(error) + } + } + } + + func makeCoordinator() -> Coordinator { + Coordinator(self) + } + + class Coordinator: NSObject, NSSharingServicePickerDelegate { + private let parent: SharingServicePicker + + init(_ parent: SharingServicePicker) { + self.parent = parent + } + + func sharingServicePicker(_ sharingServicePicker: NSSharingServicePicker, didChoose _: NSSharingService?) { + sharingServicePicker.delegate = nil + parent.isPresented = false + } + } + } + +#endif diff --git a/ApplicationLibrary/Views/Abstract/ViewBuilder.swift b/ApplicationLibrary/Views/Abstract/ViewBuilder.swift new file mode 100644 index 0000000000000000000000000000000000000000..e13c1e42e462476a805d47b1bde5115b7697e144 --- /dev/null +++ b/ApplicationLibrary/Views/Abstract/ViewBuilder.swift @@ -0,0 +1,6 @@ +import Foundation +import SwiftUI + +public func viewBuilder(@ViewBuilder _ builder: () -> some View) -> some View { + builder() +} diff --git a/ApplicationLibrary/Views/Abstract/ViewCompat.swift b/ApplicationLibrary/Views/Abstract/ViewCompat.swift new file mode 100644 index 0000000000000000000000000000000000000000..3454686cb5f373f45472193f3d120b6b0eb013e5 --- /dev/null +++ b/ApplicationLibrary/Views/Abstract/ViewCompat.swift @@ -0,0 +1,23 @@ +import SwiftUI + +public extension View { + func onChangeCompat(of value: some Equatable, _ action: @escaping () -> Void) -> some View { +// if #available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *) { +// return onChange(of: value, action) +// } else { + onChange(of: value) { _ in + action() + } +// } + } + + func onChangeCompat<V>(of value: V, _ action: @escaping (_ newValue: V) -> Void) -> some View where V: Equatable { +// if #available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *) { +// return onChange(of: value) { _, newValue in +// action(newValue) +// } +// } else { + onChange(of: value, perform: action) +// } + } +} diff --git a/ApplicationLibrary/Views/Dashboard/ActiveDashboardView.swift b/ApplicationLibrary/Views/Dashboard/ActiveDashboardView.swift new file mode 100644 index 0000000000000000000000000000000000000000..daff14dd36e449c079235499cc032373a90c3fa5 --- /dev/null +++ b/ApplicationLibrary/Views/Dashboard/ActiveDashboardView.swift @@ -0,0 +1,142 @@ +import Foundation +import Libbox +import Library +import SwiftUI + +@MainActor +public struct ActiveDashboardView: View { + @Environment(\.scenePhase) var scenePhase + @Environment(\.selection) private var parentSelection + @EnvironmentObject private var environments: ExtensionEnvironments + @EnvironmentObject private var profile: ExtensionProfile + @State private var isLoading = true + @State private var profileList: [ProfilePreview] = [] + @State private var selectedProfileID: Int64 = 0 + @State private var alert: Alert? + @State private var selection = DashboardPage.overview + @State private var systemProxyAvailable = false + @State private var systemProxyEnabled = false + + public init() {} + public var body: some View { + if isLoading { + ProgressView().onAppear { + Task { + await doReload() + } + } + } else { + if ApplicationLibrary.inPreview { + body1 + } else { + body1 + .onAppear { + Task { + await doReloadSystemProxy() + } + } + .onChangeCompat(of: profile.status) { newStatus in + if newStatus == .connected { + Task { + await doReloadSystemProxy() + } + } + } + } + } + } + + private var body1: some View { + VStack { + #if os(iOS) || os(tvOS) + if ApplicationLibrary.inPreview || profile.status.isConnectedStrict { + Picker("Page", selection: $selection) { + ForEach(DashboardPage.allCases) { page in + page.label + } + } + .pickerStyle(.segmented) + #if os(iOS) + .padding([.leading, .trailing]) + .navigationBarTitleDisplayMode(.inline) + #endif + TabView(selection: $selection) { + ForEach(DashboardPage.allCases) { page in + page.contentView($profileList, $selectedProfileID, $systemProxyAvailable, $systemProxyEnabled) + .tag(page) + } + } + .tabViewStyle(.page(indexDisplayMode: .always)) + } else { + OverviewView($profileList, $selectedProfileID, $systemProxyAvailable, $systemProxyEnabled) + } + #elseif os(macOS) + OverviewView($profileList, $selectedProfileID, $systemProxyAvailable, $systemProxyEnabled) + #endif + } + .onReceive(environments.profileUpdate) { _ in + Task { + await doReload() + } + } + .onReceive(environments.selectedProfileUpdate) { _ in + Task { + selectedProfileID = await SharedPreferences.selectedProfileID.get() + if profile.status.isConnected { + await doReloadSystemProxy() + } + } + } + .alertBinding($alert) + } + + private func doReload() async { + defer { + isLoading = false + } + if ApplicationLibrary.inPreview { + profileList = [ + ProfilePreview(Profile(id: 0, name: "profile local", type: .local, path: "")), + ProfilePreview(Profile(id: 1, name: "profile remote", type: .remote, path: "", lastUpdated: Date(timeIntervalSince1970: 0))), + ] + systemProxyAvailable = true + systemProxyEnabled = true + selectedProfileID = 0 + + } else { + do { + profileList = try await ProfileManager.list().map { ProfilePreview($0) } + if profileList.isEmpty { + return + } + selectedProfileID = await SharedPreferences.selectedProfileID.get() + if profileList.filter({ profile in + profile.id == selectedProfileID + }) + .isEmpty { + selectedProfileID = profileList[0].id + await SharedPreferences.selectedProfileID.set(selectedProfileID) + } + + } catch { + alert = Alert(error) + return + } + } + environments.emptyProfiles = profileList.isEmpty + } + + private nonisolated func doReloadSystemProxy() async { + do { + let status = try LibboxNewStandaloneCommandClient()!.getSystemProxyStatus() + await MainActor.run { + systemProxyAvailable = status.available + systemProxyEnabled = status.enabled + } + } catch { + await MainActor.run { + alert = Alert(error) + } + } + } +} diff --git a/ApplicationLibrary/Views/Dashboard/ClashModeView.swift b/ApplicationLibrary/Views/Dashboard/ClashModeView.swift new file mode 100644 index 0000000000000000000000000000000000000000..0c6941546641a36f9976f4ed57a15bc0aaf51105 --- /dev/null +++ b/ApplicationLibrary/Views/Dashboard/ClashModeView.swift @@ -0,0 +1,61 @@ +import Libbox +import Library +import SwiftUI + +@MainActor +public struct ClashModeView: View { + @Environment(\.scenePhase) private var scenePhase + @StateObject private var commandClient = CommandClient(.clashMode) + @State private var clashMode = "" + @State private var alert: Alert? + + public init() {} + public var body: some View { + VStack { + if commandClient.clashModeList.count > 1 { + Picker("", selection: Binding(get: { + clashMode + }, set: { newMode in + clashMode = newMode + Task { + await setClashMode(newMode) + } + }), content: { + ForEach(commandClient.clashModeList, id: \.self) { it in + Text(it) + } + }) + .pickerStyle(.segmented) + .padding([.top], 8) + } + } + .onReceive(commandClient.$clashMode) { newMode in + clashMode = newMode + } + .padding([.leading, .trailing]) + .onAppear { + commandClient.connect() + } + .onDisappear { + commandClient.disconnect() + } + .onChangeCompat(of: scenePhase) { newValue in + if newValue == .active { + commandClient.connect() + } else { + commandClient.disconnect() + } + } + .alertBinding($alert) + } + + private nonisolated func setClashMode(_ newMode: String) async { + do { + try LibboxNewStandaloneCommandClient()!.setClashMode(newMode) + } catch { + await MainActor.run { + alert = Alert(error) + } + } + } +} diff --git a/ApplicationLibrary/Views/Dashboard/DashboardPage.swift b/ApplicationLibrary/Views/Dashboard/DashboardPage.swift new file mode 100644 index 0000000000000000000000000000000000000000..d0a22457359222ce5b9b2fe7a91aa721603f6772 --- /dev/null +++ b/ApplicationLibrary/Views/Dashboard/DashboardPage.swift @@ -0,0 +1,44 @@ +import Foundation +import Library +import SwiftUI + +public enum DashboardPage: Int, CaseIterable, Identifiable { + public var id: Self { + self + } + + case overview + case groups +} + +public extension DashboardPage { + var title: String { + switch self { + case .overview: + return NSLocalizedString("Overview", comment: "") + case .groups: + return NSLocalizedString("Groups", comment: "") + } + } + + var label: some View { + switch self { + case .overview: + return Label("Overview", systemImage: "text.and.command.macwindow") + case .groups: + return Label("Groups", systemImage: "rectangle.3.group.fill") + } + } + + @MainActor + func contentView(_ profileList: Binding<[ProfilePreview]>, _ selectedProfileID: Binding<Int64>, _ systemProxyAvailable: Binding<Bool>, _ systemProxyEnabled: Binding<Bool>) -> some View { + viewBuilder { + switch self { + case .overview: + OverviewView(profileList, selectedProfileID, systemProxyAvailable, systemProxyEnabled) + case .groups: + GroupListView() + } + } + } +} diff --git a/ApplicationLibrary/Views/Dashboard/DashboardView.swift b/ApplicationLibrary/Views/Dashboard/DashboardView.swift new file mode 100644 index 0000000000000000000000000000000000000000..5c4423d88986616a8e5dfd60d9085dbf3dd85dcb --- /dev/null +++ b/ApplicationLibrary/Views/Dashboard/DashboardView.swift @@ -0,0 +1,115 @@ +import Libbox +import Library +import SwiftUI + +@MainActor +public struct DashboardView: View { + #if os(macOS) + @Environment(\.controlActiveState) private var controlActiveState + @State private var isLoading = true + @State private var systemExtensionInstalled = true + #endif + + public init() {} + public var body: some View { + viewBuilder { + #if os(macOS) + if Variant.useSystemExtension { + viewBuilder { + if !systemExtensionInstalled { + FormView { + InstallSystemExtensionButton { + await reload() + } + } + } else { + DashboardView0() + } + }.onAppear { + Task { + await reload() + } + } + } else { + DashboardView0() + } + #else + DashboardView0() + #endif + } + #if os(macOS) + .onChangeCompat(of: controlActiveState) { newValue in + if newValue != .inactive { + if Variant.useSystemExtension { + if !isLoading { + Task { + await reload() + } + } + } + } + } + #endif + } + + #if os(macOS) + private nonisolated func reload() async { + let systemExtensionInstalled = await SystemExtension.isInstalled() + await MainActor.run { + self.systemExtensionInstalled = systemExtensionInstalled + isLoading = false + } + } + #endif + + struct DashboardView0: View { + @EnvironmentObject private var environments: ExtensionEnvironments + + var body: some View { + if ApplicationLibrary.inPreview { + ActiveDashboardView() + } else if environments.extensionProfileLoading { + ProgressView() + } else if let profile = environments.extensionProfile { + DashboardView1().environmentObject(profile) + } else { + FormView { + InstallProfileButton { + await environments.reload() + } + } + } + } + } + + struct DashboardView1: View { + @EnvironmentObject private var environments: ExtensionEnvironments + @EnvironmentObject private var profile: ExtensionProfile + @State private var alert: Alert? + + var body: some View { + VStack { + ActiveDashboardView() + } + .alertBinding($alert) + .onChangeCompat(of: profile.status) { newValue in + if newValue == .disconnecting || newValue == .connected { + Task { + await checkServiceError() + } + } + } + } + + private nonisolated func checkServiceError() async { + var error: NSError? + let message = LibboxReadServiceError(&error) + if error != nil { + return + } + await MainActor.run { + alert = Alert(title: Text("Service Error"), message: Text(message)) + } + } + } +} diff --git a/ApplicationLibrary/Views/Dashboard/ExtensionStatusView.swift b/ApplicationLibrary/Views/Dashboard/ExtensionStatusView.swift new file mode 100644 index 0000000000000000000000000000000000000000..a525b916143c495ece8131f4cdb2674d99b53653 --- /dev/null +++ b/ApplicationLibrary/Views/Dashboard/ExtensionStatusView.swift @@ -0,0 +1,175 @@ +import Libbox +import Library +import SwiftUI + +public struct ExtensionStatusView: View { + @Environment(\.scenePhase) private var scenePhase + @StateObject private var commandClient = CommandClient(.status) + @State private var columnCount: Int = 4 + @State private var alert: Alert? + + private let infoFont = Font.system(.caption, design: .monospaced) + + public init() {} + public var body: some View { + if columnCount == 1 { + ScrollView { + body0 + } + } else { + body0 + } + } + + public var body0: some View { + viewBuilder { + VStack { + LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: columnCount), alignment: .leading) { + if ApplicationLibrary.inPreview { + StatusItem("Status") { + StatusLine("Memory", "6.4 MB") + StatusLine("Goroutines", "89") + } + StatusItem("Connections") { + StatusLine("Inbound", "34") + StatusLine("Outbound", "28") + } + StatusItem("Traffic") { + StatusLine("Uplink", "38 B/s") + StatusLine("Downlink", "249 MB/s") + } + StatusItem("TrafficTotal") { + StatusLine("Uplink", "52 MB") + StatusLine("Downlink", "5.6 GB") + } + } else if let message = commandClient.status { + StatusItem("Status") { + StatusLine("Memory", LibboxFormatMemoryBytes(message.memory)) + StatusLine("Goroutines", "\(message.goroutines)") + } + StatusItem("Connections") { + StatusLine("Inbound", "\(message.connectionsIn)") + StatusLine("Outbound", "\(message.connectionsOut)") + } + if message.trafficAvailable { + StatusItem("Traffic") { + StatusLine("Uplink", "\(LibboxFormatBytes(message.uplink))/s") + StatusLine("Downlink", "\(LibboxFormatBytes(message.downlink))/s") + } + StatusItem("Traffic Total") { + StatusLine("Uplink", LibboxFormatBytes(message.uplinkTotal)) + StatusLine("Downlink", LibboxFormatBytes(message.downlinkTotal)) + } + } + } else { + StatusItem("Status") { + StatusLine("Memory", "...") + StatusLine("Goroutines", "...") + } + StatusItem("Connections") { + StatusLine("Inbound", "...") + StatusLine("Outbound", "...") + } + } + }.background { + GeometryReader { geometry in + Rectangle() + .fill(.clear) + .frame(height: 1) + .onChangeCompat(of: geometry.size.width) { newValue in + updateColumnCount(newValue) + } + .onAppear { + updateColumnCount(geometry.size.width) + } + }.padding() + } + } + .frame(alignment: .topLeading) + .padding([.top, .leading, .trailing]) + } + .onAppear { + commandClient.connect() + } + .onDisappear { + commandClient.disconnect() + } + .onChangeCompat(of: scenePhase) { newValue in + if newValue == .active { + commandClient.connect() + } else { + commandClient.disconnect() + } + } + .alertBinding($alert) + } + + private func updateColumnCount(_ width: Double) { + let v = Int(Int(width) / 155) + let new = v <= 1 ? 1 : (v > 4 ? 4 : (v % 2 == 0 ? v : v - 1)) + + if new != columnCount { + columnCount = new + } + } + + private struct StatusItem<T>: View where T: View { + private let title: String + @ViewBuilder private let content: () -> T + + init(_ title: String, @ViewBuilder content: @escaping () -> T) { + self.title = title + self.content = content + } + + var body: some View { + VStack(alignment: .leading) { + HStack { + Text(title) + .font(.headline) + Spacer() + }.padding(.bottom, 8) + content() + } + .frame(minWidth: 125, alignment: .topLeading) + #if os(tvOS) + .padding(EdgeInsets(top: 20, leading: 26, bottom: 20, trailing: 26)) + #else + .padding(EdgeInsets(top: 10, leading: 13, bottom: 10, trailing: 13)) + #endif + .background(backgroundColor) + .cornerRadius(10) + } + + private var backgroundColor: Color { + #if os(iOS) + return Color(uiColor: .secondarySystemGroupedBackground) + #elseif os(macOS) + return Color(nsColor: .textBackgroundColor) + #elseif os(tvOS) + return Color(uiColor: .black) + #endif + } + } + + private struct StatusLine: View { + private let name: String + private let value: String + + init(_ name: String, _ value: String) { + self.name = name + self.value = value + } + + var body: some View { + HStack { + Text(name) + .font(.subheadline) + .foregroundColor(.secondary) + Spacer() + Text(value) + .font(.subheadline) + } + } + } +} diff --git a/ApplicationLibrary/Views/Dashboard/InstallProfileButton.swift b/ApplicationLibrary/Views/Dashboard/InstallProfileButton.swift new file mode 100644 index 0000000000000000000000000000000000000000..3aa7398a3e7fb5f6a0feb24f0d2dc69ef0b85e35 --- /dev/null +++ b/ApplicationLibrary/Views/Dashboard/InstallProfileButton.swift @@ -0,0 +1,32 @@ +import Library +import SwiftUI + +@MainActor +public struct InstallProfileButton: View { + @State private var alert: Alert? + + private let callback: () async -> Void + public init(_ callback: @escaping (() async -> Void)) { + self.callback = callback + } + + public var body: some View { + FormButton { + Task { + await installProfile() + } + } label: { + Label("Install Network Extension", systemImage: "lock.doc.fill") + } + .alertBinding($alert) + } + + private func installProfile() async { + do { + try await ExtensionProfile.install() + await callback() + } catch { + alert = Alert(error) + } + } +} diff --git a/ApplicationLibrary/Views/Dashboard/InstallSystemExtensionButton.swift b/ApplicationLibrary/Views/Dashboard/InstallSystemExtensionButton.swift new file mode 100644 index 0000000000000000000000000000000000000000..7e6c0b7b4548ef209041b70a682af508e203b050 --- /dev/null +++ b/ApplicationLibrary/Views/Dashboard/InstallSystemExtensionButton.swift @@ -0,0 +1,39 @@ +#if os(macOS) + + import Library + import SwiftUI + + @MainActor + public struct InstallSystemExtensionButton: View { + @State private var alert: Alert? + private let callback: () async -> Void + public init(_ callback: @escaping () async -> Void) { + self.callback = callback + } + + public var body: some View { + FormButton { + Task { + await installSystemExtension() + } + } label: { + Label("Install System Extension", systemImage: "lock.doc.fill") + } + .alertBinding($alert) + } + + private func installSystemExtension() async { + do { + if let result = try await SystemExtension.install() { + if result == .willCompleteAfterReboot { + alert = Alert(errorMessage: "Need Reboot") + } + } + await callback() + } catch { + alert = Alert(error) + } + } + } + +#endif diff --git a/ApplicationLibrary/Views/Dashboard/OverviewView.swift b/ApplicationLibrary/Views/Dashboard/OverviewView.swift new file mode 100644 index 0000000000000000000000000000000000000000..059fd75909cabf72d810e4dc5a12ba722afb06d2 --- /dev/null +++ b/ApplicationLibrary/Views/Dashboard/OverviewView.swift @@ -0,0 +1,114 @@ +import Foundation +import Libbox +import Library +import SwiftUI + +@MainActor +public struct OverviewView: View { + @Environment(\.selection) private var selection + @EnvironmentObject private var environments: ExtensionEnvironments + @EnvironmentObject private var profile: ExtensionProfile + @Binding private var profileList: [ProfilePreview] + @Binding private var selectedProfileID: Int64 + @Binding private var systemProxyAvailable: Bool + @Binding private var systemProxyEnabled: Bool + @State private var alert: Alert? + @State private var reasserting = false + + private var selectedProfileIDLocal: Binding<Int64> { + $selectedProfileID.withSetter { newValue in + reasserting = true + Task { [self] in + await switchProfile(newValue) + } + } + } + + public init(_ profileList: Binding<[ProfilePreview]>, _ selectedProfileID: Binding<Int64>, _ systemProxyAvailable: Binding<Bool>, _ systemProxyEnabled: Binding<Bool>) { + _profileList = profileList + _selectedProfileID = selectedProfileID + _systemProxyAvailable = systemProxyAvailable + _systemProxyEnabled = systemProxyEnabled + } + + public var body: some View { + VStack { + if ApplicationLibrary.inPreview || profile.status.isConnected { + ExtensionStatusView() + ClashModeView() + } + if profileList.isEmpty { + Text("Empty profiles") + } else { + FormView { + #if os(iOS) || os(tvOS) + StartStopButton() + if ApplicationLibrary.inPreview || profile.status.isConnectedStrict, systemProxyAvailable { + Toggle("HTTP Proxy", isOn: $systemProxyEnabled) + .onChangeCompat(of: systemProxyEnabled) { newValue in + Task { + await setSystemProxyEnabled(newValue) + } + } + } + Section("Profile") { + Picker(selection: selectedProfileIDLocal) { + ForEach(profileList, id: \.id) { profile in + Text(profile.name).tag(profile.id) + } + } label: {} + .pickerStyle(.inline) + } + #elseif os(macOS) + if ApplicationLibrary.inPreview || profile.status.isConnectedStrict, systemProxyAvailable { + Toggle("HTTP Proxy", isOn: $systemProxyEnabled) + .onChangeCompat(of: systemProxyEnabled) { newValue in + Task { + await setSystemProxyEnabled(newValue) + } + } + } + Section("Profile") { + ForEach(profileList, id: \.id) { profile in + Picker(profile.name, selection: selectedProfileIDLocal) { + Text("").tag(profile.id) + } + } + .pickerStyle(.radioGroup) + } + #endif + } + } + } + .alertBinding($alert) + .disabled(!ApplicationLibrary.inPreview && (!profile.status.isSwitchable || reasserting)) + } + + private func switchProfile(_ newProfileID: Int64) async { + await SharedPreferences.selectedProfileID.set(newProfileID) + environments.selectedProfileUpdate.send() + if profile.status.isConnected { + do { + try await serviceReload() + } catch { + alert = Alert(error) + } + } + reasserting = false + } + + private nonisolated func serviceReload() async throws { + try LibboxNewStandaloneCommandClient()!.serviceReload() + } + + private nonisolated func setSystemProxyEnabled(_ isEnabled: Bool) async { + do { + try LibboxNewStandaloneCommandClient()!.setSystemProxyEnabled(isEnabled) + await SharedPreferences.systemProxyEnabled.set(isEnabled) + } catch { + await MainActor.run { + alert = Alert(error) + } + } + } +} diff --git a/ApplicationLibrary/Views/Dashboard/StartStopButton.swift b/ApplicationLibrary/Views/Dashboard/StartStopButton.swift new file mode 100644 index 0000000000000000000000000000000000000000..dd781b9adc12a6018dd3081f1912ab985da58d71 --- /dev/null +++ b/ApplicationLibrary/Views/Dashboard/StartStopButton.swift @@ -0,0 +1,92 @@ +import Library +import NetworkExtension +import SwiftUI + +@MainActor +public struct StartStopButton: View { + @EnvironmentObject private var environments: ExtensionEnvironments + + public init() {} + + public var body: some View { + viewBuilder { + if ApplicationLibrary.inPreview { + #if os(iOS) || os(tvOS) + Toggle(isOn: .constant(true)) { + Text("Enabled") + } + #elseif os(macOS) + Button {} label: { + Label("Stop", systemImage: "stop.fill") + } + #endif + + } else if let profile = environments.extensionProfile { + Button0().environmentObject(profile) + } else { + #if os(iOS) || os(tvOS) + Toggle(isOn: .constant(false)) { + Text("Enabled") + } + #elseif os(macOS) + Button {} label: { + Label("Start", systemImage: "play.fill") + } + .disabled(true) + #endif + } + } + .disabled(environments.emptyProfiles) + } + + private struct Button0: View { + @EnvironmentObject private var environments: ExtensionEnvironments + @EnvironmentObject private var profile: ExtensionProfile + @State private var alert: Alert? + + var body: some View { + viewBuilder { + #if os(iOS) || os(tvOS) + Toggle(isOn: Binding(get: { + profile.status.isConnected + }, set: { newValue, _ in + Task { + await switchProfile(newValue) + } + })) { + Text("Enabled") + } + #elseif os(macOS) + Button { + Task { + await switchProfile(!profile.status.isConnected) + } + } label: { + if !profile.status.isConnected { + Label("Start", systemImage: "play.fill") + } else { + Label("Stop", systemImage: "stop.fill") + } + } + #endif + } + .disabled(!profile.status.isEnabled) + .alertBinding($alert) + } + + private nonisolated func switchProfile(_ isEnabled: Bool) async { + do { + if isEnabled { + try await profile.start() + await environments.logClient.connect() + } else { + try await profile.stop() + } + } catch { + await MainActor.run { + alert = Alert(error) + } + } + } + } +} diff --git a/ApplicationLibrary/Views/EnvironmentValues.swift b/ApplicationLibrary/Views/EnvironmentValues.swift new file mode 100644 index 0000000000000000000000000000000000000000..7daaa43ceba0aa288348ec98e2912f2983c6dd66 --- /dev/null +++ b/ApplicationLibrary/Views/EnvironmentValues.swift @@ -0,0 +1,58 @@ +import Foundation +import Libbox +import Library +import SwiftUI + +public extension EnvironmentValues { + private struct showMenuBarExtraKey: EnvironmentKey { + static let defaultValue: Binding<Bool> = .constant(true) + } + + var showMenuBarExtra: Binding<Bool> { + get { + self[showMenuBarExtraKey.self] + } + set { + self[showMenuBarExtraKey.self] = newValue + } + } + + private struct selectionKey: EnvironmentKey { + static let defaultValue: Binding<NavigationPage> = .constant(.dashboard) + } + + var selection: Binding<NavigationPage> { + get { + self[selectionKey.self] + } + set { + self[selectionKey.self] = newValue + } + } + + private struct importRemoteProfileKey: EnvironmentKey { + static var defaultValue: Binding<LibboxImportRemoteProfile?> = .constant(nil) + } + + var importRemoteProfile: Binding<LibboxImportRemoteProfile?> { + get { + self[importRemoteProfileKey.self] + } + set { + self[importRemoteProfileKey.self] = newValue + } + } + + private struct importProfileKey: EnvironmentKey { + static var defaultValue: Binding<LibboxProfileContent?> = .constant(nil) + } + + var importProfile: Binding<LibboxProfileContent?> { + get { + self[importProfileKey.self] + } + set { + self[importProfileKey.self] = newValue + } + } +} diff --git a/ApplicationLibrary/Views/Groups/GroupItemView.swift b/ApplicationLibrary/Views/Groups/GroupItemView.swift new file mode 100644 index 0000000000000000000000000000000000000000..075dce853bb63d121f4da4c010bcaf51405e4914 --- /dev/null +++ b/ApplicationLibrary/Views/Groups/GroupItemView.swift @@ -0,0 +1,94 @@ +import Libbox +import Library +import SwiftUI + +@MainActor +public struct GroupItemView: View { + private let _group: Binding<OutboundGroup> + private var group: OutboundGroup { + _group.wrappedValue + } + + private let item: OutboundGroupItem + public init(_ group: Binding<OutboundGroup>, _ item: OutboundGroupItem) { + _group = group + self.item = item + } + + @State private var alert: Alert? + + public var body: some View { + Button { + if group.selectable, group.selected != item.tag { + Task { + await selectOutbound() + } + } + } label: { + HStack { + VStack { + HStack { + Text(item.tag) + .font(.caption) + .foregroundStyle(.foreground) + .lineLimit(1) + .truncationMode(.tail) + Spacer(minLength: 0) + } + Spacer(minLength: 8) + HStack { + Text(item.displayType) + .font(.caption) + .foregroundColor(.secondary) + Spacer(minLength: 0) + } + } + Spacer(minLength: 0) + VStack { + if group.selected == item.tag { + Image(systemName: "checkmark.circle.fill") + .foregroundStyle(Color.accentColor) + } + Spacer(minLength: 0) + if item.urlTestDelay > 0 { + Text(item.delayString) + .font(.caption) + .foregroundColor(item.delayColor) + } + } + } + } + #if !os(tvOS) + .buttonStyle(.borderless) + .padding(EdgeInsets(top: 10, leading: 13, bottom: 10, trailing: 13)) + .background(backgroundColor) + .cornerRadius(10) + #endif + .alertBinding($alert) + } + + private nonisolated func selectOutbound() async { + do { + try await LibboxNewStandaloneCommandClient()!.selectOutbound(group.tag, outboundTag: item.tag) + var newGroup = await group + newGroup.selected = item.tag + await MainActor.run { [newGroup] in + _group.wrappedValue = newGroup + } + } catch { + await MainActor.run { + alert = Alert(error) + } + } + } + + private var backgroundColor: Color { + #if os(iOS) + return Color(uiColor: .secondarySystemGroupedBackground) + #elseif os(macOS) + return Color(nsColor: .textBackgroundColor) + #elseif os(tvOS) + return Color.black + #endif + } +} diff --git a/ApplicationLibrary/Views/Groups/GroupListView.swift b/ApplicationLibrary/Views/Groups/GroupListView.swift new file mode 100644 index 0000000000000000000000000000000000000000..6fbb432e91a029acde0b2bf8a441183321eb825c --- /dev/null +++ b/ApplicationLibrary/Views/Groups/GroupListView.swift @@ -0,0 +1,81 @@ +import Libbox +import Library +import SwiftUI + +public struct GroupListView: View { + @Environment(\.scenePhase) private var scenePhase + @State private var isLoading = true + @StateObject private var commandClient = CommandClient(.groups) + @State private var groups: [OutboundGroup] = [] + + public init() {} + public var body: some View { + VStack { + if isLoading { + Text("Loading...") + } else if !groups.isEmpty { + ScrollView { + VStack { + ForEach(groups, id: \.hashValue) { it in + GroupView(it) + } + }.padding() + } + } else { + Text("Empty groups") + } + } + .onAppear { + connect() + } + .onDisappear { + commandClient.disconnect() + } + .onChangeCompat(of: scenePhase) { newValue in + if newValue == .active { + commandClient.connect() + } else { + commandClient.disconnect() + } + } + .onReceive(commandClient.$groups, perform: { groups in + if let groups { + setGroups(groups) + } + }) + } + + private func connect() { + if ApplicationLibrary.inPreview { + groups = [ + OutboundGroup(tag: "my_group", type: "selector", selected: "server", selectable: true, isExpand: true, items: [ + OutboundGroupItem(tag: "server", type: "Shadowsocks", urlTestTime: .now, urlTestDelay: 12), + OutboundGroupItem(tag: "server2", type: "WireGuard", urlTestTime: .now, urlTestDelay: 34), + OutboundGroupItem(tag: "auto", type: "URLTest", urlTestTime: .now, urlTestDelay: 100), + ]), + OutboundGroup(tag: "group2", type: "urltest", selected: "client", selectable: true, isExpand: false, items: + (0 ..< 234).map { index in + OutboundGroupItem(tag: "client\(index)", type: "Shadowsocks", urlTestTime: .now, urlTestDelay: UInt16(100 + index * 10)) + }), + ] + isLoading = false + } else { + commandClient.connect() + } + } + + private func setGroups(_ goGroups: [LibboxOutboundGroup]) { + var groups = [OutboundGroup]() + for goGroup in goGroups { + var items = [OutboundGroupItem]() + let itemIterator = goGroup.getItems()! + while itemIterator.hasNext() { + let goItem = itemIterator.next()! + items.append(OutboundGroupItem(tag: goItem.tag, type: goItem.type, urlTestTime: Date(timeIntervalSince1970: Double(goItem.urlTestTime)), urlTestDelay: UInt16(goItem.urlTestDelay))) + } + groups.append(OutboundGroup(tag: goGroup.tag, type: goGroup.type, selected: goGroup.selected, selectable: goGroup.selectable, isExpand: goGroup.isExpand, items: items)) + } + self.groups = groups + isLoading = false + } +} diff --git a/ApplicationLibrary/Views/Groups/GroupView.swift b/ApplicationLibrary/Views/Groups/GroupView.swift new file mode 100644 index 0000000000000000000000000000000000000000..8d06140dbd0fbccae28f7bc4e80c7c3074a28158 --- /dev/null +++ b/ApplicationLibrary/Views/Groups/GroupView.swift @@ -0,0 +1,168 @@ +import Libbox +import Library +import SwiftUI + +@MainActor +public struct GroupView: View { + @State private var group: OutboundGroup + @State private var geometryWidth: CGFloat = 300 + @State private var alert: Alert? + + public init(_ group: OutboundGroup) { + _group = State(initialValue: group) + } + + private var title: some View { + HStack { + Text(group.tag) + .font(.headline) + Text(group.displayType) + .font(.subheadline) + .foregroundColor(.secondary) + Text("\(group.items.count)") + .font(.subheadline) + .padding(EdgeInsets(top: 2, leading: 4, bottom: 2, trailing: 4)) + .background(Color.gray.opacity(0.5)) + .cornerRadius(4) + Button { + group.isExpand = !group.isExpand + Task { + await setGroupExpand() + } + } label: { + if group.isExpand { + Image(systemName: "arrow.down.to.line") + } else { + Image(systemName: "arrow.up.to.line") + } + } + #if os(macOS) || os(tvOS) + .buttonStyle(.plain) + #endif + Button { + Task { + await doURLTest() + } + } label: { + Image(systemName: "bolt.fill") + } + #if os(macOS) || os(tvOS) + .buttonStyle(.plain) + #endif + } + .alertBinding($alert) + .padding([.top, .bottom], 8) + } + + public var body: some View { + Section { + if group.isExpand { + LazyVGrid(columns: Array(repeating: GridItem(.flexible()), + count: explandColumnCount())) + { + ForEach(group.items, id: \.tag) { it in + GroupItemView($group, it) + } + } + } else { + VStack(spacing: 5) { + ForEach(Array(itemGroups.enumerated()), id: \.offset) { items in + HStack(spacing: 5) { + ForEach(items.element, id: \.tag) { it in + ZStack { + Rectangle() + .fill(it.delayColor) + if it.tag == group.selected { + Rectangle() + .fill(Color.white) + #if !os(tvOS) + .frame(width: 5, height: 5) + #else + .frame(width: 15, height: 15) + #endif + } + } + #if !os(tvOS) + .frame(width: 10, height: 10) + #else + .frame(width: 30, height: 30) + #endif + } + }.frame(maxWidth: .infinity, alignment: .topLeading) + } + } + } + } header: { + title + .frame(maxWidth: .infinity, alignment: .topLeading) + } + .background { + GeometryReader { geometry in + Rectangle() + .fill(.clear) + .frame(height: 1) + .onChangeCompat(of: geometry.size.width) { newValue in + geometryWidth = newValue + } + .onAppear { + geometryWidth = geometry.size.width + } + }.padding() + } + } + + private var itemGroups: [[OutboundGroupItem]] { + let count: Int + #if os(tvOS) + count = Int(Int(geometryWidth) / 40) + #else + count = Int(Int(geometryWidth) / 20) + #endif + if count == 0 { + return [group.items] + } else { + return group.items.chunked( + into: count + ) + } + } + + private func explandColumnCount() -> Int { + let standardCount = Int(Int(geometryWidth) / 180) + #if os(iOS) + return standardCount < 2 ? 2 : standardCount + #elseif os(tvOS) + return 4 + #else + return standardCount < 1 ? 1 : standardCount + #endif + } + + private nonisolated func doURLTest() async { + do { + try await LibboxNewStandaloneCommandClient()!.urlTest(group.tag) + } catch { + await MainActor.run { + alert = Alert(error) + } + } + } + + private nonisolated func setGroupExpand() async { + do { + try await LibboxNewStandaloneCommandClient()!.setGroupExpand(group.tag, isExpand: group.isExpand) + } catch { + await MainActor.run { + alert = Alert(error) + } + } + } +} + +private extension Array { + func chunked(into size: Int) -> [[Element]] { + stride(from: 0, to: count, by: size).map { + Array(self[$0 ..< Swift.min($0 + size, count)]) + } + } +} diff --git a/ApplicationLibrary/Views/Groups/OutboundGroup.swift b/ApplicationLibrary/Views/Groups/OutboundGroup.swift new file mode 100644 index 0000000000000000000000000000000000000000..1e38a3951df542225229960e7c5b0f3fbada2be4 --- /dev/null +++ b/ApplicationLibrary/Views/Groups/OutboundGroup.swift @@ -0,0 +1,27 @@ +import Foundation +import Libbox +import SwiftUI + +public struct OutboundGroup: Codable { + let tag: String + let type: String + var selected: String + let selectable: Bool + var isExpand: Bool + let items: [OutboundGroupItem] + + var hashValue: Int { + var value = tag.hashValue + (value, _) = value.addingReportingOverflow(selected.hashValue) + for item in items { + (value, _) = value.addingReportingOverflow(item.urlTestTime.hashValue) + } + return value + } +} + +public extension OutboundGroup { + var displayType: String { + LibboxProxyDisplayType(type) + } +} diff --git a/ApplicationLibrary/Views/Groups/OutboundGroupItem.swift b/ApplicationLibrary/Views/Groups/OutboundGroupItem.swift new file mode 100644 index 0000000000000000000000000000000000000000..bdceb5a3994bc269435f54a54871f75744a68d6c --- /dev/null +++ b/ApplicationLibrary/Views/Groups/OutboundGroupItem.swift @@ -0,0 +1,34 @@ +import Foundation +import Libbox +import SwiftUI + +public struct OutboundGroupItem: Codable { + public let tag: String + public let type: String + + public let urlTestTime: Date + public let urlTestDelay: UInt16 +} + +public extension OutboundGroupItem { + var displayType: String { + LibboxProxyDisplayType(type) + } + + var delayString: String { + "\(urlTestDelay)ms" + } + + var delayColor: Color { + switch urlTestDelay { + case 0: + return .gray + case ..<800: + return .green + case 800 ..< 1500: + return .yellow + default: + return .orange + } + } +} diff --git a/ApplicationLibrary/Views/Log/LogView.swift b/ApplicationLibrary/Views/Log/LogView.swift new file mode 100644 index 0000000000000000000000000000000000000000..5b6963fb314a5808cc2c56cdc55c3d2d00eb2acf --- /dev/null +++ b/ApplicationLibrary/Views/Log/LogView.swift @@ -0,0 +1,90 @@ +import Library +import SwiftUI + +public struct LogView: View { + @Environment(\.selection) private var selection + @EnvironmentObject private var environments: ExtensionEnvironments + + public init() {} + + public var body: some View { + LogView0().environmentObject(environments.logClient) + } + + private struct LogView0: View { + @EnvironmentObject private var environments: ExtensionEnvironments + @EnvironmentObject private var logClient: CommandClient + private let logFont = Font.system(.caption2, design: .monospaced) + + var body: some View { + if ApplicationLibrary.inPreview { + let logList = [ + "(packet-tunnel) log server started", + "INFO[0000] router: loaded geoip database: 250 codes", + "INFO[0000] router: loaded geosite database: 1400 codes", + "INFO[0000] router: updated default interface en0, index 11", + "inbound/tun[0]: started at utun3", + "sing-box started (1.666s)", + ] + ScrollView { + VStack(alignment: .leading, spacing: 0) { + ForEach(Array(logList.enumerated()), id: \.offset) { it in + Text(it.element) + .font(logFont) + #if os(tvOS) + .focusable() + #endif + Spacer(minLength: 8) + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + .padding() + } + #if os(tvOS) + .focusEffectDisabled() + .focusSection() + #endif + } else if logClient.logList.isEmpty { + VStack { + if logClient.isConnected { + Text("Empty logs") + } else { + Text("Service not started").onAppear { + environments.connectLog() + } + } + } + } else { + ScrollViewReader { reader in + ScrollView { + VStack(alignment: .leading, spacing: 0) { + ForEach(Array(logClient.logList.enumerated()), id: \.offset) { it in + Text(it.element) + .font(logFont) + #if os(tvOS) + .focusable() + #endif + Spacer(minLength: 8) + } + + .onChangeCompat(of: logClient.logList.count) { newCount in + withAnimation { + reader.scrollTo(newCount - 1) + } + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + .padding() + } + #if os(tvOS) + .focusEffectDisabled() + .focusSection() + #endif + .onAppear { + reader.scrollTo(logClient.logList.count - 1) + } + } + } + } + } +} diff --git a/ApplicationLibrary/Views/NavigationPage.swift b/ApplicationLibrary/Views/NavigationPage.swift new file mode 100644 index 0000000000000000000000000000000000000000..a0cec65aa2b1ab8dcf4e9957683a5e2d640c08c4 --- /dev/null +++ b/ApplicationLibrary/Views/NavigationPage.swift @@ -0,0 +1,99 @@ +import Foundation +import Library +import SwiftUI + +public enum NavigationPage: Int, CaseIterable, Identifiable { + public var id: Self { + self + } + + case dashboard + #if os(macOS) + case groups + #endif + case logs + case profiles + case settings +} + +public extension NavigationPage { + #if os(macOS) + static var macosDefaultPages: [NavigationPage] { + [.logs, .profiles, .settings] + } + #endif + + var label: some View { + Label(title, systemImage: iconImage) + .tint(.textColor) + } + + var title: String { + switch self { + case .dashboard: + return NSLocalizedString("Dashboard", comment: "") + #if os(macOS) + case .groups: + return NSLocalizedString("Groups", comment: "") + #endif + case .logs: + return NSLocalizedString("Logs", comment: "") + case .profiles: + return NSLocalizedString("Profiles", comment: "") + case .settings: + return NSLocalizedString("Settings", comment: "") + } + } + + private var iconImage: String { + switch self { + case .dashboard: + return "text.and.command.macwindow" + #if os(macOS) + case .groups: + return "rectangle.3.group.fill" + #endif + case .logs: + return "doc.text.fill" + case .profiles: + return "list.bullet.rectangle.fill" + case .settings: + return "gear.circle.fill" + } + } + + @MainActor + var contentView: some View { + viewBuilder { + switch self { + case .dashboard: + DashboardView() + #if os(macOS) + case .groups: + GroupListView() + #endif + case .logs: + LogView() + case .profiles: + ProfileView() + case .settings: + SettingView() + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) + #if os(iOS) + .background(Color(uiColor: .systemGroupedBackground)) + #endif + } + + #if os(macOS) + func visible(_ profile: ExtensionProfile?) -> Bool { + switch self { + case .groups: + return profile?.status.isConnectedStrict == true + default: + return true + } + } + #endif +} diff --git a/ApplicationLibrary/Views/Profile/EditProfileContentView.swift b/ApplicationLibrary/Views/Profile/EditProfileContentView.swift new file mode 100644 index 0000000000000000000000000000000000000000..8e16499e8d8ec271de9a98e38123e329a88ac4a4 --- /dev/null +++ b/ApplicationLibrary/Views/Profile/EditProfileContentView.swift @@ -0,0 +1,154 @@ +#if os(iOS) || os(macOS) + import Foundation + import Library + import SwiftUI + + @MainActor + public struct EditProfileContentView: View { + public struct Context: Codable, Hashable { + public let profileID: Int64 + public let readOnly: Bool + } + + private let profileID: Int64? + private let readOnly: Bool + + public init(_ context: Context?) { + profileID = context?.profileID + readOnly = context?.readOnly == true + } + + @Environment(\.dismiss) private var dismiss + + @State private var isLoading = true + @State private var profile: Profile! + @State private var profileContent = "" + @State private var isChanged = false + @State private var alert: Alert? + + public var body: some View { + viewBuilder { + if isLoading { + ProgressView().onAppear { + Task { + await loadContent() + } + } + } else { + viewBuilder { + if readOnly { + TextEditor(text: .constant(profileContent)) + } else { + TextEditor(text: $profileContent) + } + } + .font(Font.system(.caption2, design: .monospaced)) + .autocorrectionDisabled(true) + // https://stackoverflow.com/questions/66721935/swiftui-how-to-disable-the-smart-quotes-in-texteditor + // https://stackoverflow.com/questions/74034171/textfield-with-autocorrectiondisabled-still-shows-predictive-text-bar + .textContentType(.init(rawValue: "")) + #if os(iOS) + .keyboardType(.asciiCapable) + .textInputAutocapitalization(.none) + .background(Color(UIColor.secondarySystemGroupedBackground)) + #elseif os(macOS) + .padding() + #endif + .onChangeCompat(of: profileContent) { + isChanged = true + } + } + } + .alertBinding($alert) + .navigationTitle(navigationTitle) + #if os(macOS) + .toolbar { + ToolbarItemGroup(placement: .navigation) { + if !readOnly { + Button { + Task { + await saveContent() + } + } label: { + Label("Save", image: "save") + } + .disabled(!isChanged) + } else { + Button { + NSPasteboard.general.setString(profileContent, forType: .fileContents) + } label: { + Label("Copy", systemImage: "clipboard.fill") + } + } + } + } + #elseif os(iOS) + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + if !readOnly { + Button("Save") { + Task { + await saveContent() + } + }.disabled(!isChanged) + } else { + Button("Copy") { + UIPasteboard.general.string = profileContent + } + } + } + } + .navigationBarTitleDisplayMode(.inline) + #endif + } + + private var navigationTitle: String { + if readOnly { + return "View Content" + } else { + return "Edit Content" + } + } + + private func loadContent() async { + do { + try await loadContentBackground() + } catch { + alert = Alert(error) + } + isLoading = false + } + + private nonisolated func loadContentBackground() async throws { + guard let profileID else { + throw NSError(domain: "Context destroyed", code: 0) + } + guard let profile = try await ProfileManager.get(profileID) else { + throw NSError(domain: "Profile missing", code: 0) + } + let profileContent = try profile.read() + await MainActor.run { + self.profile = profile + self.profileContent = profileContent + } + } + + private func saveContent() async { + guard let profile else { + return + } + do { + try await saveContentBackground(profile) + } catch { + alert = Alert(error) + return + } + isChanged = false + } + + private nonisolated func saveContentBackground(_ profile: Profile) async throws { + try await profile.write(profileContent) + } + } + +#endif diff --git a/ApplicationLibrary/Views/Profile/EditProfileView.swift b/ApplicationLibrary/Views/Profile/EditProfileView.swift new file mode 100644 index 0000000000000000000000000000000000000000..2f4f6053e2fc3ddea4d13123dca0d4501ca31cf9 --- /dev/null +++ b/ApplicationLibrary/Views/Profile/EditProfileView.swift @@ -0,0 +1,177 @@ +import Libbox +import Library +import SwiftUI + +@MainActor +public struct EditProfileView: View { + @EnvironmentObject private var environments: ExtensionEnvironments + @Environment(\.dismiss) private var dismiss + @EnvironmentObject private var profile: Profile + + @State private var isLoading = false + @State private var isChanged = false + @State private var alert: Alert? + @State private var shareLinkPresented = false + @State private var shareLinkText: String? + + public init() {} + public var body: some View { + FormView { + FormItem("Name") { + TextField("Name", text: $profile.name, prompt: Text("Required")) + .multilineTextAlignment(.trailing) + } + + Picker(selection: $profile.type) { + Text("Local").tag(ProfileType.local) + Text("iCloud").tag(ProfileType.icloud) + Text("Remote").tag(ProfileType.remote) + } label: { + Text("Type") + } + .disabled(true) + if profile.type == .icloud { + FormItem("Path") { + TextField("Path", text: $profile.path, prompt: Text("Required")) + .multilineTextAlignment(.trailing) + } + } else if profile.type == .remote { + FormItem("URL") { + TextField("URL", text: $profile.remoteURL.unwrapped(""), prompt: Text("Required")) + .multilineTextAlignment(.trailing) + } + Toggle("Auto Update", isOn: $profile.autoUpdate) + FormItem("Auto Update Interval") { + TextField("Auto Update Interval", text: $profile.autoUpdateInterval.stringBinding(defaultValue: 60), prompt: Text("In Minutes")) + .multilineTextAlignment(.trailing) + #if os(iOS) + .keyboardType(.numberPad) + #endif + } + } + if profile.type == .remote { + Section("Status") { + FormTextItem("Last Updated", profile.lastUpdatedString) + } + } + Section("Action") { + if profile.type != .remote { + #if os(iOS) || os(macOS) + FormNavigationLink { + EditProfileContentView(EditProfileContentView.Context(profileID: profile.id!, readOnly: false)) + } label: { + Label("Edit Content", systemImage: "pencil") + .foregroundColor(.accentColor) + } + #endif + } else { + #if os(iOS) || os(macOS) + FormNavigationLink { + EditProfileContentView(EditProfileContentView.Context(profileID: profile.id!, readOnly: true)) + } label: { + Label("View Content", systemImage: "doc.fill") + .foregroundColor(.accentColor) + } + #endif + FormButton { + isLoading = true + Task { + await updateProfile() + } + } label: { + Label("Update", systemImage: "arrow.clockwise") + } + .foregroundColor(.accentColor) + .disabled(isLoading) + } + FormButton(role: .destructive) { + Task { + await deleteProfile() + } + } label: { + Label("Delete", systemImage: "trash.fill") + } + .foregroundColor(.red) + } + } + .onChangeCompat(of: profile.name) { + isChanged = true + } + .onChangeCompat(of: profile.remoteURL) { + isChanged = true + } + .onChangeCompat(of: profile.autoUpdate) { + isChanged = true + } + .disabled(isLoading) + #if os(macOS) + .toolbar { + ToolbarItemGroup(placement: .navigation) { + Button { + isLoading = true + Task { + await saveProfile() + } + } label: { + Image("save", bundle: ApplicationLibrary.bundle, label: Text("Save")) + } + .disabled(isLoading || !isChanged) + } + } + #elseif os(iOS) + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button("Save") { + isLoading = true + Task { + await saveProfile() + } + }.disabled(!isChanged) + } + } + #endif + .alertBinding($alert) + .navigationTitle("Edit Profile") + } + + private func updateProfile() async { + defer { + isLoading = false + } + do { + try await Task.sleep(nanoseconds: UInt64(100 * Double(NSEC_PER_MSEC))) + try await profile.updateRemoteProfile() + environments.profileUpdate.send() + } catch { + alert = Alert(error) + } + } + + private func deleteProfile() async { + do { + try await ProfileManager.delete(profile) + } catch { + alert = Alert(error) + return + } + environments.profileUpdate.send() + dismiss() + } + + private func saveProfile() async { + do { + _ = try await ProfileManager.update(profile) + #if os(iOS) || os(tvOS) + try await UIProfileUpdateTask.configure() + #else + try await ProfileUpdateTask.configure() + #endif + } catch { + alert = Alert(error) + return + } + isChanged = false + isLoading = false + environments.profileUpdate.send() + } +} diff --git a/ApplicationLibrary/Views/Profile/ImportProfileView.swift b/ApplicationLibrary/Views/Profile/ImportProfileView.swift new file mode 100644 index 0000000000000000000000000000000000000000..8440f2c1bca26f541509289b5bfe87351f039b2c --- /dev/null +++ b/ApplicationLibrary/Views/Profile/ImportProfileView.swift @@ -0,0 +1,186 @@ +#if os(tvOS) + + import DeviceDiscoveryUI + import Libbox + import Library + import SwiftUI + + @MainActor + public struct ImportProfileView: View { + @Environment(\.dismiss) private var dismiss + + @State private var isLoading = false + @State private var selected = false + @State private var alert: Alert? + @State private var connection: NWSocket? + @State private var profiles: [LibboxProfilePreview]? + @State private var isImporting = false + private let callback: () async -> Void + + public init(callback: @escaping () async -> Void) { + self.callback = callback + } + + public var body: some View { + VStack(alignment: .center) { + if !selected { + Form { + Section { + EmptyView() + } footer: { + Text("To import configurations from your iPhone or iPad, make sure sing-box is the **same version** on both devices and **VPN is disabled**.") + } + + DevicePicker( + .applicationService(name: "sing-box:profile")) + { endpoint in + selected = true + Task { + await handleEndpoint(endpoint) + } + } label: { + Text("Select Device") + } fallback: { + EmptyView() + } parameters: { + .applicationService + } + } + } else if let profiles { + Form { + Section { + EmptyView() + } footer: { + Text("\(profiles.count) Profiles") + } + ForEach(profiles, id: \.profileID) { profile in + Button(profile.name) { + isLoading = true + Task { + selectProfile(profileID: profile.profileID) + isLoading = false + } + }.disabled(isLoading || isImporting) + } + } + } else { + Text("Connecting...") + } + } + .focusSection() + .alertBinding($alert) + .navigationTitle("Import Profile") + } + + private func reset() { + selected = false + profiles = nil + } + + private func handleEndpoint(_ endpoint: NWEndpoint) async { + let connection = NWConnection(to: endpoint, using: NWParameters.applicationService) + self.connection = NWSocket(connection) + connection.start(queue: .global()) + do { + try await loopMessages() + } catch { + alert = Alert(error) + reset() + } + } + + private nonisolated func loopMessages() async throws { + guard let connection = await connection else { + return + } + var message: Data + while true { + do { + message = try connection.read() + } catch { + throw NSError(domain: "read from connection: \(error.localizedDescription)", code: 0) + } + var error: NSError? + switch Int64(message[0]) { + case LibboxMessageTypeError: + let message = LibboxDecodeErrorMessage(message, &error) + if let error { + throw error + } + if let message { + throw NSError(domain: "remote error: \(message.message)", code: 0) + } + case LibboxMessageTypeProfileList: + let decoder = LibboxProfileDecoder() + try decoder.decode(message) + let iterator = decoder.iterator()! + var profiles = [LibboxProfilePreview]() + while iterator.hasNext() { + let profile = iterator.next()! + if profile.type == LibboxProfileTypeiCloud { + // not supported on tvOS + continue + } + profiles.append(profile) + } + await MainActor.run { [self, profiles] in + self.profiles = profiles + isImporting = false + } + case LibboxMessageTypeProfileContent: + let content = LibboxDecodeProfileContent(message, &error) + if let error { + throw error + } + try await importProfile(content!) + default: + throw NSError(domain: "unknown message type \(message[0])", code: 0) + } + } + } + + private func selectProfile(profileID: Int64) { + guard let connection else { + return + } + let request = LibboxProfileContentRequest() + request.profileID = profileID + do { + try connection.write(request.encode()) + isImporting = true + } catch { + alert = Alert(error) + reset() + } + } + + private nonisolated func importProfile(_ content: LibboxProfileContent) async throws { + var type: ProfileType = .local + switch content.type { + case LibboxProfileTypeLocal: + type = .local + case LibboxProfileTypeiCloud: + type = .icloud + case LibboxProfileTypeRemote: + type = .remote + default: + break + } + let nextProfileID = try await ProfileManager.nextID() + let profileConfigDirectory = FilePath.sharedDirectory.appendingPathComponent("configs", isDirectory: true) + try FileManager.default.createDirectory(at: profileConfigDirectory, withIntermediateDirectories: true) + let profileConfig = profileConfigDirectory.appendingPathComponent("config_\(nextProfileID).json") + try content.config.write(to: profileConfig, atomically: true, encoding: .utf8) + var lastUpdated: Date? + if content.lastUpdated > 0 { + lastUpdated = Date(timeIntervalSince1970: Double(content.lastUpdated)) + } + try await ProfileManager.create(Profile(name: content.name, type: type, path: profileConfig.relativePath, remoteURL: content.remotePath, autoUpdate: content.autoUpdate, lastUpdated: lastUpdated)) + await callback() + await MainActor.run { + dismiss() + } + } + } + +#endif diff --git a/ApplicationLibrary/Views/Profile/NewProfileView.swift b/ApplicationLibrary/Views/Profile/NewProfileView.swift new file mode 100644 index 0000000000000000000000000000000000000000..91cd5ea0cfd48516d5b77c007574ac6c4fa357fd --- /dev/null +++ b/ApplicationLibrary/Views/Profile/NewProfileView.swift @@ -0,0 +1,252 @@ +import Foundation +import Libbox +import Library +import SwiftUI + +@MainActor +public struct NewProfileView: View { + @EnvironmentObject private var environments: ExtensionEnvironments + @Environment(\.dismiss) private var dismiss + + @State private var isSaving = false + @State private var profileName = "" + @State private var profileType = ProfileType.local + @State private var fileImport = false + @State private var fileURL: URL! + @State private var remotePath = "" + @State private var autoUpdate = true + @State private var autoUpdateInterval: Int32 = 60 + @State private var pickerPresented = false + @State private var alert: Alert? + + public struct ImportRequest: Codable, Hashable { + public let name: String + public let url: String + } + + public init(_ importRequest: ImportRequest? = nil) { + if let importRequest { + _profileName = .init(initialValue: importRequest.name) + _profileType = .init(initialValue: .remote) + _remotePath = .init(initialValue: importRequest.url) + } + } + + public var body: some View { + FormView { + FormItem("Name") { + TextField("Name", text: $profileName, prompt: Text("Required")) + .multilineTextAlignment(.trailing) + } + Picker(selection: $profileType) { + #if !os(tvOS) + Text("Local").tag(ProfileType.local) + Text("iCloud").tag(ProfileType.icloud) + #endif + Text("Remote").tag(ProfileType.remote) + } label: { + Text("Type") + } + if profileType == .local { + Picker(selection: $fileImport) { + Text("Create New").tag(false) + Text("Import").tag(true) + } label: { + Text("File") + } + #if os(tvOS) + .disabled(true) + #endif + viewBuilder { + if fileImport { + HStack { + Text("File Path") + Spacer() + Spacer() + if let fileURL { + Button(fileURL.fileName) { + pickerPresented = true + } + } else { + Button("Choose") { + pickerPresented = true + } + } + } + } + } + } else if profileType == .icloud { + FormItem("Path") { + TextField("Path", text: $remotePath, prompt: Text("Required")) + .multilineTextAlignment(.trailing) + } + } else if profileType == .remote { + FormItem("URL") { + TextField("URL", text: $remotePath, prompt: Text("Required")) + .multilineTextAlignment(.trailing) + } + Toggle("Auto Update", isOn: $autoUpdate) + FormItem("Auto Update Interval") { + TextField("Auto Update Interval", text: $autoUpdateInterval.stringBinding(defaultValue: 60), prompt: Text("In Minutes")) + .multilineTextAlignment(.trailing) + #if os(iOS) + .keyboardType(.numberPad) + #endif + } + } + Section { + if !isSaving { + FormButton { + isSaving = true + Task { + await createProfile() + } + } label: { + Label("Create", systemImage: "doc.fill.badge.plus") + } + } else { + ProgressView() + } + } + } + .navigationTitle("New Profile") + .alertBinding($alert) + #if os(iOS) || os(macOS) + .fileImporter( + isPresented: $pickerPresented, + allowedContentTypes: [.json], + allowsMultipleSelection: false + ) { result in + do { + let urls = try result.get() + if !urls.isEmpty { + fileURL = urls[0] + } + } catch { + alert = Alert(error) + return + } + } + #endif + } + + private func createProfile() async { + defer { + isSaving = false + } + if profileName.isEmpty { + alert = Alert(errorMessage: "Missing profile name") + return + } + if remotePath.isEmpty { + if profileType == .icloud { + alert = Alert(errorMessage: "Missing path") + return + } else if profileType == .remote { + alert = Alert(errorMessage: "Missing URL") + return + } + } + do { + try await createProfileBackground() + } catch { + alert = Alert(error) + return + } + environments.profileUpdate.send() + dismiss() + #if os(macOS) + resetFields() + #endif + } + + private func resetFields() { + profileName = "" + profileType = .local + fileImport = false + fileURL = nil + remotePath = "" + } + + private nonisolated func createProfileBackground() async throws { + let nextProfileID = try await ProfileManager.nextID() + + var savePath = "" + var remoteURL: String? = nil + var lastUpdated: Date? = nil + + let profileName = await profileName + let profileType = await profileType + let fileImport = await fileImport + let fileURL = await fileURL + let remotePath = await remotePath + let autoUpdate = await autoUpdate + let autoUpdateInterval = await autoUpdateInterval + + if profileType == .local { + let profileConfigDirectory = FilePath.sharedDirectory.appendingPathComponent("configs", isDirectory: true) + try FileManager.default.createDirectory(at: profileConfigDirectory, withIntermediateDirectories: true) + let profileConfig = profileConfigDirectory.appendingPathComponent("config_\(nextProfileID).json") + if fileImport { + guard let fileURL else { + throw NSError(domain: "Missing file", code: 0) + } + if !fileURL.startAccessingSecurityScopedResource() { + throw NSError(domain: "Missing access to selected file", code: 0) + } + defer { + fileURL.stopAccessingSecurityScopedResource() + } + try String(contentsOf: fileURL).write(to: profileConfig, atomically: true, encoding: .utf8) + } else { + try "{}".write(to: profileConfig, atomically: true, encoding: .utf8) + } + savePath = profileConfig.relativePath + } else if profileType == .icloud { + if !FileManager.default.fileExists(atPath: FilePath.iCloudDirectory.path) { + try FileManager.default.createDirectory(at: FilePath.iCloudDirectory, withIntermediateDirectories: true) + } + let saveURL = FilePath.iCloudDirectory.appendingPathComponent(remotePath, isDirectory: false) + _ = saveURL.startAccessingSecurityScopedResource() + defer { + saveURL.stopAccessingSecurityScopedResource() + } + do { + _ = try String(contentsOf: saveURL) + } catch { + try "{}".write(to: saveURL, atomically: true, encoding: .utf8) + } + savePath = remotePath + } else if profileType == .remote { + let remoteContent = try HTTPClient().getString(remotePath) + var error: NSError? + LibboxCheckConfig(remoteContent, &error) + if let error { + throw error + } + let profileConfigDirectory = FilePath.sharedDirectory.appendingPathComponent("configs", isDirectory: true) + try FileManager.default.createDirectory(at: profileConfigDirectory, withIntermediateDirectories: true) + let profileConfig = profileConfigDirectory.appendingPathComponent("config_\(nextProfileID).json") + try remoteContent.write(to: profileConfig, atomically: true, encoding: .utf8) + savePath = profileConfig.relativePath + remoteURL = remotePath + lastUpdated = .now + } + try await ProfileManager.create(Profile( + name: profileName, + type: profileType, + path: savePath, + remoteURL: remoteURL, + autoUpdate: autoUpdate, + autoUpdateInterval: autoUpdateInterval, + lastUpdated: lastUpdated + )) + if profileType == .remote { + #if os(iOS) || os(tvOS) + try await UIProfileUpdateTask.configure() + #else + try await ProfileUpdateTask.configure() + #endif + } + } +} diff --git a/ApplicationLibrary/Views/Profile/ProfileView.swift b/ApplicationLibrary/Views/Profile/ProfileView.swift new file mode 100644 index 0000000000000000000000000000000000000000..2b3b0b16238072cc2f2662123494563a8c1f970a --- /dev/null +++ b/ApplicationLibrary/Views/Profile/ProfileView.swift @@ -0,0 +1,424 @@ +import Foundation +import Libbox +import Library +import Network +import QRCode +import SwiftUI + +@MainActor +public struct ProfileView: View { + @EnvironmentObject private var environments: ExtensionEnvironments + @Environment(\.importProfile) private var importProfile + @Environment(\.importRemoteProfile) private var importRemoteProfile + @State private var importRemoteProfileRequest: NewProfileView.ImportRequest? + @State private var importRemoteProfilePresented = false + + @State private var isLoading = true + @State private var isUpdating = false + + @State private var alert: Alert? + @State private var profileList: [ProfilePreview] = [] + + #if os(iOS) || os(tvOS) + @State private var editMode = EditMode.inactive + #endif + + #if os(tvOS) + @Environment(\.devicePickerSupports) private var devicePickerSupports + #endif + + public init() {} + public var body: some View { + VStack { + if isLoading { + ProgressView().onAppear { + Task { + await doReload() + } + } + } else { + ZStack { + if let importRemoteProfileRequest { + NavigationDestinationCompat(isPresented: $importRemoteProfilePresented) { + NewProfileView(importRemoteProfileRequest) + } + } + FormView { + #if os(iOS) + FormNavigationLink { + NewProfileView() + } label: { + Text("New Profile").foregroundColor(.accentColor) + } + .disabled(editMode.isEditing) + #elseif os(macOS) + FormNavigationLink { + NewProfileView() + } label: { + Text("New Profile") + } + #elseif os(tvOS) + Section { + FormNavigationLink { + NewProfileView() + } label: { + Text("New Profile").foregroundColor(.accentColor) + } + if ApplicationLibrary.inPreview || devicePickerSupports(.applicationService(name: "sing-box"), parameters: { .applicationService }) { + FormNavigationLink { + ImportProfileView { + await doReload() + } + } label: { + Text("Import Profile").foregroundColor(.accentColor) + } + } + } + #endif + if profileList.isEmpty { + Text("Empty profiles") + } else { + List { + ForEach(profileList, id: \.id) { profile in + viewBuilder { + #if os(iOS) || os(tvOS) + if editMode.isEditing == true { + Text(profile.name) + } else { + ProfileItem(self, profile) + } + #else + ProfileItem(self, profile) + #endif + } + } + .onMove(perform: moveProfile) + .onDelete(perform: deleteProfile) + } + } + } + } + } + } + .disabled(isUpdating) + .alertBinding($alert, $isLoading) + .onAppear { + if let profile = importProfile.wrappedValue { + importProfile.wrappedValue = nil + createImportProfileDialog(profile) + } + if let remoteProfile = importRemoteProfile.wrappedValue { + importRemoteProfile.wrappedValue = nil + createImportRemoteProfileDialog(remoteProfile) + } + } + .onChangeCompat(of: importProfile.wrappedValue) { newValue in + if let newValue { + importProfile.wrappedValue = nil + createImportProfileDialog(newValue) + } + } + .onChangeCompat(of: importRemoteProfile.wrappedValue) { newValue in + if let newValue { + importRemoteProfile.wrappedValue = nil + createImportRemoteProfileDialog(newValue) + } + } + .onReceive(environments.profileUpdate) { _ in + Task { + await doReload() + } + } + #if os(iOS) + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + EditButton().disabled(profileList.isEmpty) + } + } + #elseif os(tvOS) + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + if editMode == .inactive { + Button("Edit") { + editMode = .active + } + .disabled(profileList.isEmpty) + } else { + Button("Done") { + editMode = .inactive + } + } + } + } + #endif + #if os(iOS) || os(tvOS) + .environment(\.editMode, $editMode) + #endif + } + + private func createImportProfileDialog(_ profile: LibboxProfileContent) { + alert = Alert( + title: Text("Import Profile"), + message: Text("Are you sure to import profile \(profile.name)?"), + primaryButton: .default(Text("Import")) { + Task { + do { + try await profile.importProfile() + } catch { + alert = Alert(error) + return + } + await doReload() + } + }, + secondaryButton: .cancel() + ) + } + + private func createImportRemoteProfileDialog(_ newValue: LibboxImportRemoteProfile) { + importRemoteProfileRequest = .init(name: newValue.name, url: newValue.url) + alert = Alert( + title: Text("Import Remote Profile"), + message: Text("Are you sure to import remote profile \(newValue.name)? You will connect to \(newValue.host) to download the configuration."), + primaryButton: .default(Text("Import")) { + importRemoteProfilePresented = true + }, + secondaryButton: .cancel() + ) + } + + private func doReload() async { + if ApplicationLibrary.inPreview { + profileList = [ + ProfilePreview(Profile(id: 0, name: "profile local", type: .local, path: "")), + ProfilePreview(Profile(id: 1, name: "profile remote", type: .remote, path: "", lastUpdated: Date(timeIntervalSince1970: 0))), + ] + } else { + defer { + isLoading = false + } + do { + profileList = try await ProfileManager.list().map { ProfilePreview($0) } + } catch { + alert = Alert(error) + return + } + } + environments.emptyProfiles = profileList.isEmpty + } + + private func updateProfile(_ profile: Profile) async { + await updateProfileBackground(profile) + isUpdating = false + } + + private nonisolated func updateProfileBackground(_ profile: Profile) async { + do { + _ = try await profile.updateRemoteProfile() + } catch { + await MainActor.run { + alert = Alert(error) + } + } + } + + private func deleteProfile(_ profile: Profile) async { + do { + _ = try await ProfileManager.delete(profile) + } catch { + alert = Alert(error) + return + } + environments.profileUpdate.send() + } + + private func moveProfile(from source: IndexSet, to destination: Int) { + profileList.move(fromOffsets: source, toOffset: destination) + for (index, profile) in profileList.enumerated() { + profileList[index].order = UInt32(index) + profile.origin.order = UInt32(index) + } + Task { + do { + try await ProfileManager.update(profileList.map(\.origin)) + } catch { + alert = Alert(error) + } + environments.profileUpdate.send() + } + } + + private func deleteProfile(where profileIndex: IndexSet) { + let profileToDelete = profileIndex.map { index in + profileList[index].origin + } + profileList.remove(atOffsets: profileIndex) + environments.emptyProfiles = profileList.isEmpty + Task { + do { + _ = try await ProfileManager.delete(profileToDelete) + } catch { + alert = Alert(error) + } + environments.profileUpdate.send() + } + } + + @MainActor + public struct ProfileItem: View { + private let parent: ProfileView + @State private var profile: ProfilePreview + @State private var shareLinkPresented = false + + public init(_ parent: ProfileView, _ profile: ProfilePreview) { + self.parent = parent + _profile = State(initialValue: profile) + } + + public var body: some View { + #if os(iOS) || os(macOS) + if #available(iOS 16.0, macOS 13.0,*) { + body0.draggable(profile.origin) + } else { + body0 + } + #else + body0 + #endif + } + + private var body0: some View { + viewBuilder { + #if !os(macOS) + FormNavigationLink { + EditProfileView().environmentObject(profile.origin) + } label: { + Text(profile.name) + } + .sheet(isPresented: $shareLinkPresented) { + shareLinkView.padding() + } + .contextMenu { + ProfileShareButton(parent.$alert, profile.origin) { + Label("Share", systemImage: "square.and.arrow.up.fill") + } + + if profile.type == .remote { + Button { + shareLinkPresented = true + } label: { + Label("Share URL as QR Code", systemImage: "qrcode") + } + Button { + parent.isUpdating = true + Task { + await parent.updateProfile(profile.origin) + profile = ProfilePreview(profile.origin) + } + } label: { + Label("Update", systemImage: "arrow.clockwise") + } + } + Button(role: .destructive) { + Task { + await parent.deleteProfile(profile.origin) + } + } label: { + Label("Delete", systemImage: "trash.fill") + } + } + #else + FormNavigationLink { + EditProfileView().environmentObject(profile.origin) + } label: { + HStack { + VStack(alignment: .leading) { + Text(profile.name) + if profile.type == .remote { + Spacer(minLength: 4) + Text("Last Updated: \(profile.origin.lastUpdatedString)").font(.caption) + } + } + HStack { + if profile.type == .remote { + Button { + parent.isUpdating = true + Task { + await parent.updateProfile(profile.origin) + profile = ProfilePreview(profile.origin) + } + } label: { + Image(systemName: "arrow.clockwise") + } + .padding(.leading, 4) + + Button { + shareLinkPresented = true + } label: { + Image(systemName: "qrcode") + } + .padding(.leading, 4) + .popover(isPresented: $shareLinkPresented, arrowEdge: .bottom) { + shareLinkView + } + } + ProfileShareButton(parent.$alert, profile.origin) { + Image(systemName: "square.and.arrow.up.fill") + } + .padding(.leading, 4) + Button { + Task { + await parent.deleteProfile(profile.origin) + } + } label: { + Image(systemName: "trash.fill") + } + .padding([.leading, .trailing], 4) + } + .buttonStyle(.plain) + .frame(maxWidth: .infinity, alignment: .trailing) + } + .padding(.vertical, 8) + .frame(maxWidth: .infinity, alignment: .leading) + } + #endif + } + } + + private var shareLinkView: some View { + #if os(iOS) + viewBuilder { + if #available(iOS 16.0, *) { + shareLinkView0 + .presentationDetents([.medium]) + .presentationDragIndicator(.visible) + } else { + shareLinkView0 + } + } + #elseif os(macOS) + shareLinkView0 + .frame(minWidth: 300, minHeight: 300) + #else + shareLinkView0 + #endif + } + + private var foregroundColor: CGColor { + #if canImport(UIKit) + return UIColor.label.cgColor + #elseif canImport(AppKit) + return NSColor.labelColor.cgColor + #endif + } + + private var shareLinkView0: some View { + QRCodeViewUI( + content: LibboxGenerateRemoteProfileImportLink(profile.name, profile.remoteURL!), + errorCorrection: .low, + foregroundColor: foregroundColor, + backgroundColor: CGColor(gray: 1.0, alpha: 0.0) + ) + } + } +} diff --git a/ApplicationLibrary/Views/Setting/CoreView.swift b/ApplicationLibrary/Views/Setting/CoreView.swift new file mode 100644 index 0000000000000000000000000000000000000000..e731d2837525aa292a0ad0aa6241f6a514273da6 --- /dev/null +++ b/ApplicationLibrary/Views/Setting/CoreView.swift @@ -0,0 +1,95 @@ + +import Libbox +import Library +import SwiftUI + +public struct CoreView: View { + @State private var isLoading = true + + @State private var version = "" + @State private var dataSize = "" + + public init() {} + public var body: some View { + viewBuilder { + if isLoading { + ProgressView().onAppear { + Task { + await loadSettings() + } + } + } else { + FormView { + FormTextItem("Version", version) + FormTextItem("Data Size", dataSize) + + Section("Working Directory") { + #if os(macOS) + FormButton { + NSWorkspace.shared.selectFile(nil, inFileViewerRootedAtPath: FilePath.workingDirectory.relativePath) + } label: { + Label("Open", systemImage: "macwindow.and.cursorarrow") + } + #endif + FormButton(role: .destructive) { + Task { + await destroyWorkingDirectory() + } + } label: { + Label("Destroy", systemImage: "trash.fill") + } + .foregroundColor(.red) + } + } + } + } + .navigationTitle("Core") + #if os(iOS) + .navigationBarTitleDisplayMode(.inline) + #endif + } + + private nonisolated func loadSettings() async { + if ApplicationLibrary.inPreview { + version = "<redacted>" + dataSize = LibboxFormatBytes(1000 * 1000 * 10) + isLoading = false + } else { + version = LibboxVersion() + dataSize = "Loading..." + isLoading = false + await loadSettingsBackground() + } + } + + private nonisolated func loadSettingsBackground() async { + let dataSize = (try? FilePath.workingDirectory.formattedSize()) ?? "Unknown" + await MainActor.run { + self.dataSize = dataSize + } + } + + private nonisolated func destroyWorkingDirectory() async { + try? FileManager.default.removeItem(at: FilePath.workingDirectory) + await MainActor.run { + isLoading = true + } + } +} + +private extension URL { + func formattedSize() throws -> String? { + guard let urls = FileManager.default.enumerator(at: self, includingPropertiesForKeys: nil)?.allObjects as? [URL] else { + return nil + } + let size = try urls.lazy.reduce(0) { + try ($1.resourceValues(forKeys: [.totalFileAllocatedSizeKey]).totalFileAllocatedSize ?? 0) + $0 + } + let formatter = ByteCountFormatter() + formatter.countStyle = .file + guard let byteCount = formatter.string(for: size) else { + return nil + } + return byteCount + } +} diff --git a/ApplicationLibrary/Views/Setting/MacAppView.swift b/ApplicationLibrary/Views/Setting/MacAppView.swift new file mode 100644 index 0000000000000000000000000000000000000000..24b0619722eac6581421b7770bdcf664d4bc2056 --- /dev/null +++ b/ApplicationLibrary/Views/Setting/MacAppView.swift @@ -0,0 +1,155 @@ +#if os(macOS) + + import AppKit + import Library + import ServiceManagement + import SwiftUI + + public struct MacAppView: View { + @State private var isLoading = true + + @State private var startAtLogin = false + @Environment(\.showMenuBarExtra) private var showMenuBarExtra + @State private var menuBarExtraInBackground = false + + @State private var alert: Alert? + + public init() {} + public var body: some View { + viewBuilder { + if isLoading { + ProgressView().onAppear { + Task { + await loadSettings() + } + } + } else { + FormView { + FormSection { + Toggle("Start At Login", isOn: $startAtLogin) + .onChangeCompat(of: startAtLogin) { newValue in + Task { + updateLoginItems(newValue) + } + } + } footer: { + Text("Launch the application when the system is logged in. If enabled at the same time as `Show in Menu Bar` and `Keep Menu Bar in Background`, the application interface will not be opened automatically.") + } + + Toggle("Show in Menu Bar", isOn: showMenuBarExtra) + .onChangeCompat(of: showMenuBarExtra.wrappedValue) { newValue in + Task { + await SharedPreferences.showMenuBarExtra.set(newValue) + if !newValue { + menuBarExtraInBackground = false + } + } + } + + if showMenuBarExtra.wrappedValue { + Toggle("Keep Menu Bar in Background", isOn: $menuBarExtraInBackground) + .onChangeCompat(of: menuBarExtraInBackground) { newValue in + Task { + await SharedPreferences.menuBarExtraInBackground.set(newValue) + } + } + } + + if Variant.useSystemExtension { + Section("System Extension") { + FormButton { + Task { + await updateSystemExtension() + } + } label: { + Label("Update", systemImage: "arrow.down.doc.fill") + } + FormButton(role: .destructive) { + Task { + await uninstallSystemExtension() + } + } label: { + Label("Uninstall", systemImage: "trash.fill").foregroundColor(.red) + } + } + } + } + } + } + .alertBinding($alert) + .navigationTitle("App") + #if os(iOS) + .navigationBarTitleDisplayMode(.inline) + #endif + } + + private func loadSettings() async { + startAtLogin = SMAppService.mainApp.status == .enabled + menuBarExtraInBackground = await SharedPreferences.menuBarExtraInBackground.get() + isLoading = false + } + + private func updateLoginItems(_ startAtLogin: Bool) { + do { + if startAtLogin { + if SMAppService.mainApp.status == .enabled { + try? SMAppService.mainApp.unregister() + } + + try SMAppService.mainApp.register() + } else { + try SMAppService.mainApp.unregister() + } + } catch { + alert = Alert(error) + } + } + + private func updateSystemExtension() async { + do { + if let result = try await SystemExtension.install(forceUpdate: true) { + switch result { + case .completed: + alert = Alert( + title: Text("Update"), + message: Text("System Extension updated."), + dismissButton: .default(Text("Ok")) {} + ) + case .willCompleteAfterReboot: + alert = Alert( + title: Text("Update"), + message: Text("Reboot required."), + dismissButton: .default(Text("Ok")) {} + ) + } + } + } catch { + alert = Alert(error) + } + } + + private func uninstallSystemExtension() async { + do { + if let result = try await SystemExtension.uninstall() { + switch result { + case .completed: + alert = Alert( + title: Text("Uninstall"), + message: Text("System Extension removed."), + dismissButton: .default(Text("Ok")) {} + ) + case .willCompleteAfterReboot: + alert = Alert( + title: Text("Uninstall"), + message: Text("Reboot required."), + dismissButton: .default(Text("Ok")) {} + ) + } + } + } catch { + alert = Alert(error) + } + } + } + +#endif diff --git a/ApplicationLibrary/Views/Setting/OnDemandRulesView.swift b/ApplicationLibrary/Views/Setting/OnDemandRulesView.swift new file mode 100644 index 0000000000000000000000000000000000000000..c06939347780ea0b7c6ae1dc0de37cbfbcdac928 --- /dev/null +++ b/ApplicationLibrary/Views/Setting/OnDemandRulesView.swift @@ -0,0 +1,57 @@ +import Foundation +import Library +import SwiftUI + +public struct OnDemandRulesView: View { + @State private var isLoading = true + @State private var alwaysOn = false + + public init() {} + public var body: some View { + viewBuilder { + if isLoading { + ProgressView().onAppear { + Task.detached { + await loadSettings() + } + } + } else { + FormView { + FormSection { + Toggle("Always On", isOn: $alwaysOn) + .onChangeCompat(of: alwaysOn) { newValue in + Task { + await SharedPreferences.alwaysOn.set(newValue) + } + } + } footer: { + Text(""" + Implement always-on via on-demand rules. + + This should not be an intended use of the API, so you cannot disable VPN in system settings. To stop the service manually, use the in-app interface or simply delete the VPN profile. + """) + } + + FormButton { + Task { + await SharedPreferences.resetOnDemandRules() + isLoading = true + } + } label: { + Label("Reset", systemImage: "eraser.fill") + } + .foregroundColor(.red) + } + } + } + .navigationTitle("On Demand Rules") + #if os(iOS) + .navigationBarTitleDisplayMode(.inline) + #endif + } + + private func loadSettings() async { + alwaysOn = await SharedPreferences.alwaysOn.get() + isLoading = false + } +} diff --git a/ApplicationLibrary/Views/Setting/PacketTunnelView.swift b/ApplicationLibrary/Views/Setting/PacketTunnelView.swift new file mode 100644 index 0000000000000000000000000000000000000000..4be49aee44c9220b07d8b91fc5beb1a273cf5f50 --- /dev/null +++ b/ApplicationLibrary/Views/Setting/PacketTunnelView.swift @@ -0,0 +1,150 @@ +import Library +import SwiftUI + +struct PacketTunnelView: View { + @State private var isLoading = true + + @State private var ignoreMemoryLimit = false + + @State private var includeAllNetworks = false + @State private var excludeAPNs = false + @State private var excludeCellularServices = false + @State private var excludeLocalNetworks = false + @State private var enforceRoutes = false + + public init() {} + public var body: some View { + viewBuilder { + if isLoading { + ProgressView().onAppear { + Task.detached { + await loadSettings() + } + } + } else { + FormView { + FormSection { + Toggle("Ignore Memory Limit", isOn: $ignoreMemoryLimit) + .onChangeCompat(of: ignoreMemoryLimit) { newValue in + Task { + await SharedPreferences.ignoreMemoryLimit.set(newValue) + } + } + } footer: { + Text("Do not enforce memory limits on sing-box. Will cause OOM on non-jailbroken iOS and tvOS devices.") + } + + #if !os(tvOS) + + FormSection { + Toggle("includeAllNetworks", isOn: $includeAllNetworks) + .onChangeCompat(of: includeAllNetworks) { newValue in + Task { + await SharedPreferences.includeAllNetworks.set(newValue) + } + } + } footer: { + Text(""" + If this property is true, the system routes network traffic through the tunnel except traffic for designated system services necessary for maintaining expected device functionality. You can exclude some types of traffic using the **excludeAPNs**, **excludeLocalNetworks**, and **excludeCellularServices** properties in combination with this property. + + when enabled, the default TUN stack is changed to `gvisor`, and the `system` and `mixed` stacks are not available. + + [Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/3131931-includeallnetworks) + """) + .multilineTextAlignment(.leading) + } + + FormSection { + Toggle("excludeAPNs", isOn: $excludeAPNs) + .onChangeCompat(of: excludeAPNs) { newValue in + Task { + await SharedPreferences.excludeAPNs.set(newValue) + } + } + } footer: { + Text(""" + If this property is true, the system excludes Apple Push Notification services (APNs) traffic, but only when the **includeAllNetworks** property is also true. + + [Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/4140516-excludeapns) + """) + } + + FormSection { + Toggle("excludeCellularServices", isOn: $excludeCellularServices) + .onChangeCompat(of: excludeCellularServices) { newValue in + Task { + await SharedPreferences.excludeCellularServices.set(newValue) + } + } + } footer: { + Text(""" + If this property is true, the system excludes cellular services — such as Wi-Fi Calling, MMS, SMS, and Visual Voicemail — but only when the **includeAllNetworks** property is also true. This property doesn’t impact services that use the cellular network only — such as VoLTE — which the system automatically excludes. + + [Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/4140517-excludecellularservices) + """) + } + + FormSection { + Toggle("excludeLocalNetworks", isOn: $excludeLocalNetworks) + .onChangeCompat(of: excludeLocalNetworks) { newValue in + Task { + await SharedPreferences.excludeLocalNetworks.set(newValue) + } + } + } footer: { + Text(""" + If this property is true, the system excludes network connections to hosts on the local network — such as AirPlay, AirDrop, and CarPlay — but only when the **includeAllNetworks** or **enforceRoutes** property is also true. + + [Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/3143658-excludelocalnetworks) + """) + } + + FormSection { + Toggle("enforceRoutes", isOn: $enforceRoutes) + .onChangeCompat(of: enforceRoutes) { newValue in + Task { + await SharedPreferences.enforceRoutes.set(newValue) + } + } + } footer: { + Text(""" + If this property is true when the **includeAllNetworks** property is false, the system scopes the included routes to the VPN and the excluded routes to the current primary network interface. This property supersedes the system routing table and scoping operations by apps. + + If you set both the **enforceRoutes** and **excludeLocalNetworks** properties to true, the system excludes network connections to hosts on the local network. + + [Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/3689459-enforceroutes) + """) + } + + #endif + + FormButton { + Task { + await SharedPreferences.resetPacketTunnel() + isLoading = true + } + } label: { + Label("Reset", systemImage: "eraser.fill") + } + .foregroundColor(.red) + } + } + } + .navigationTitle("Packet Tunnel") + #if os(iOS) + .navigationBarTitleDisplayMode(.inline) + #endif + } + + private func loadSettings() async { + ignoreMemoryLimit = await SharedPreferences.ignoreMemoryLimit.get() + #if !os(tvOS) + includeAllNetworks = await SharedPreferences.includeAllNetworks.get() + excludeAPNs = await SharedPreferences.excludeAPNs.get() + excludeCellularServices = await SharedPreferences.excludeCellularServices.get() + excludeLocalNetworks = await SharedPreferences.excludeLocalNetworks.get() + enforceRoutes = await SharedPreferences.enforceRoutes.get() + #endif + isLoading = false + } +} diff --git a/ApplicationLibrary/Views/Setting/ProfileOverrideView.swift b/ApplicationLibrary/Views/Setting/ProfileOverrideView.swift new file mode 100644 index 0000000000000000000000000000000000000000..1cd996bb424d6f722e1c890b3a47155bd2480465 --- /dev/null +++ b/ApplicationLibrary/Views/Setting/ProfileOverrideView.swift @@ -0,0 +1,78 @@ +import Library +import SwiftUI + +public struct ProfileOverrideView: View { + @State private var isLoading = true + @State private var excludeDefaultRoute = false + @State private var autoRouteUseSubRangesByDefault = false + @State private var excludeAPNsRoute = false + + public init() {} + public var body: some View { + viewBuilder { + if isLoading { + ProgressView().onAppear { + Task.detached { + await loadSettings() + } + } + } else { + FormView { + FormSection { + Toggle("Hide VPN Icon", isOn: $excludeDefaultRoute) + .onChangeCompat(of: excludeDefaultRoute) { newValue in + Task { + await SharedPreferences.excludeDefaultRoute.set(newValue) + } + } + } footer: { + Text("Append `0.0.0.0/31` to `inet4_route_exclude_address` if not exists.") + } + + FormSection { + Toggle("No Default Route", isOn: $autoRouteUseSubRangesByDefault) + .onChangeCompat(of: autoRouteUseSubRangesByDefault) { newValue in + Task { + await SharedPreferences.autoRouteUseSubRangesByDefault.set(newValue) + } + } + } footer: { + Text("By default, segment routing is used in `auto_route` instead of global routing. If `*_<route_address/route_exclude_address>` exists in the configuration, this item will not take effect on the corresponding network. (commonly used to resolve HomeKit compatibility issues)") + } + + FormSection { + Toggle("Exclude APNs Route", isOn: $excludeAPNsRoute) + .onChangeCompat(of: excludeAPNsRoute) { newValue in + Task { + await SharedPreferences.excludeAPNsRoute.set(newValue) + } + } + } footer: { + Text("Append `push.apple.com` to `bypass_domain`, and `17.0.0.0/8` to `inet4_route_exclude_address`.") + } + + FormButton { + Task { + await SharedPreferences.resetProfileOverride() + isLoading = true + } + } label: { + Label("Reset", systemImage: "eraser.fill") + } + .foregroundColor(.red) + } + } + } + .navigationTitle("Profile Override") + #if os(iOS) + .navigationBarTitleDisplayMode(.inline) + #endif + } + + private func loadSettings() async { + excludeDefaultRoute = await SharedPreferences.excludeDefaultRoute.get() + autoRouteUseSubRangesByDefault = await SharedPreferences.autoRouteUseSubRangesByDefault.get() + excludeAPNsRoute = await SharedPreferences.excludeAPNsRoute.get() + isLoading = false + } +} diff --git a/ApplicationLibrary/Views/Setting/ServiceLogView.swift b/ApplicationLibrary/Views/Setting/ServiceLogView.swift new file mode 100644 index 0000000000000000000000000000000000000000..9df8639269587109eee35dab0c2b7517cd0e5410 --- /dev/null +++ b/ApplicationLibrary/Views/Setting/ServiceLogView.swift @@ -0,0 +1,112 @@ +import Foundation +import Library +import SwiftUI + +@MainActor +public struct ServiceLogView: View { + @Environment(\.dismiss) private var dismiss + + @State private var isLoading = true + @State private var content = "" + @State private var alert: Alert? + + private let logFont = Font.system(.caption, design: .monospaced) + + public init() {} + + public var body: some View { + viewBuilder { + if isLoading { + ProgressView().onAppear { + Task { + await loadContent() + } + } + } else { + if content.isEmpty { + Text("Empty content") + } else { + ScrollView { + Text(content) + .font(logFont) + .frame(maxWidth: .infinity, alignment: .topLeading) + } + .padding() + } + } + } + .toolbar { + if !content.isEmpty { + #if !os(tvOS) + ShareButtonCompat($alert) { + Label("Export", systemImage: "square.and.arrow.up.fill") + } itemURL: { + try content.generateShareFile(name: "service.log") + } + #endif + Button(role: .destructive) { + Task { + await deleteContent() + } + } label: { + #if !os(tvOS) + Label("Delete", systemImage: "trash.fill") + #else + Image(systemName: "trash.fill") + #endif + } + } + } + .alertBinding($alert) + .navigationTitle("Service Log") + #if os(tvOS) + .focusable() + #endif + } + + private nonisolated func loadContent() async { + var content = "" + do { + content = try String(contentsOf: FilePath.cacheDirectory.appendingPathComponent("stderr.log")) + } catch {} + if content.isEmpty { + do { + content = try String(contentsOf: FilePath.cacheDirectory.appendingPathComponent("stderr.log.old")) + } catch {} + } + #if DEBUG + if content.isEmpty { + content = "Empty content" + } + #endif + if !content.isEmpty { + var systemInfo = utsname() + uname(&systemInfo) + let machineMirror = Mirror(reflecting: systemInfo.machine) + let machineName = machineMirror.children.reduce("") { identifier, element in + guard let value = element.value as? Int8, value != 0 else { return identifier } + return identifier + String(UnicodeScalar(UInt8(value))) + } + var deviceInfo = "Machine: " + machineName + "\n" + #if os(iOS) + await deviceInfo += "System: " + (UIDevice.current.systemName) + " " + (UIDevice.current.systemVersion) + "\n" + #elseif os(macOS) + deviceInfo += "System: macOS " + ProcessInfo().operatingSystemVersionString + "\n" + #endif + content = deviceInfo + "\n" + content + } + await MainActor.run { [content] in + self.content = content + isLoading = false + } + } + + private nonisolated func deleteContent() async { + try? FileManager.default.removeItem(at: FilePath.cacheDirectory.appendingPathComponent("stderr.log")) + try? FileManager.default.removeItem(at: FilePath.cacheDirectory.appendingPathComponent("stderr.log.old")) + await MainActor.run { + dismiss() + isLoading = true + } + } +} diff --git a/ApplicationLibrary/Views/Setting/SettingView.swift b/ApplicationLibrary/Views/Setting/SettingView.swift new file mode 100644 index 0000000000000000000000000000000000000000..7c1fe37907d8295e6b2d613d8b989d6dde0c63b3 --- /dev/null +++ b/ApplicationLibrary/Views/Setting/SettingView.swift @@ -0,0 +1,146 @@ + +import Library +import SwiftUI + +public struct SettingView: View { + private enum Tabs: Int, CaseIterable, Identifiable { + public var id: Self { + self + } + + #if os(macOS) + case app + #endif + + case core, packetTunnel, onDemandRules, profileOverride, sponsors + + var label: some View { + Label(title, systemImage: iconImage) + } + + var title: String { + switch self { + #if os(macOS) + case .app: + return NSLocalizedString("App", comment: "") + #endif + case .core: + return NSLocalizedString("Core", comment: "") + case .packetTunnel: + return NSLocalizedString("Packet Tunnel", comment: "") + case .onDemandRules: + return NSLocalizedString("On Demand Rules", comment: "") + case .profileOverride: + return NSLocalizedString("Profile Override", comment: "") + case .sponsors: + return NSLocalizedString("Sponsors", comment: "") + } + } + + private var iconImage: String { + switch self { + #if os(macOS) + case .app: + return "app.badge.fill" + #endif + case .core: + return "shippingbox.fill" + case .packetTunnel: + return "aspectratio.fill" + case .onDemandRules: + return "filemenu.and.selection" + case .profileOverride: + return "square.dashed.inset.filled" + case .sponsors: + return "heart.fill" + } + } + + @MainActor + var contentView: some View { + viewBuilder { + switch self { + #if os(macOS) + case .app: + MacAppView() + #endif + case .core: + CoreView() + case .packetTunnel: + PacketTunnelView() + case .onDemandRules: + OnDemandRulesView() + case .profileOverride: + ProfileOverrideView() + case .sponsors: + SponsorsView() + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) + #if os(iOS) + .background(Color(uiColor: .systemGroupedBackground)) + #endif + } + + @MainActor + var navigationLink: some View { + FormNavigationLink { + contentView + } label: { + label + } + } + } + + @State private var isLoading = true + @State private var taiwanFlagAvailable = false + + public init() {} + public var body: some View { + FormView { + #if os(macOS) + Tabs.app.navigationLink + #endif + ForEach([Tabs.core, Tabs.packetTunnel, Tabs.onDemandRules, Tabs.profileOverride]) { it in + it.navigationLink + } + #if !os(tvOS) + Section("About") { + Link(destination: URL(string: "https://sing-box.sagernet.org/")!) { + Label("Documentation", systemImage: "doc.on.doc.fill") + } + .buttonStyle(.plain) + .foregroundColor(.accentColor) + RequestReviewButton { + Label("Rate on the App Store", systemImage: "text.bubble.fill") + } + #if os(macOS) + if Variant.useSystemExtension { + Tabs.sponsors.navigationLink + } + #endif + } + #endif + Section("Debug") { + FormNavigationLink { + ServiceLogView() + } label: { + Label("Service Log", systemImage: "doc.on.clipboard") + } + FormTextItem("Taiwan Flag Available", "touchid") { + if isLoading { + Text("Loading...") + .onAppear { + Task.detached { + taiwanFlagAvailable = !DeviceCensorship.isChinaDevice() + isLoading = false + } + } + } else { + Text(taiwanFlagAvailable.description) + } + } + } + } + } +} diff --git a/ApplicationLibrary/Views/Setting/SponsorsView.swift b/ApplicationLibrary/Views/Setting/SponsorsView.swift new file mode 100644 index 0000000000000000000000000000000000000000..60061f7b63b99881ad441c9dcae383d6c2d2ddd8 --- /dev/null +++ b/ApplicationLibrary/Views/Setting/SponsorsView.swift @@ -0,0 +1,29 @@ +import Foundation +import SwiftUI + +public struct SponsorsView: View { + @Environment(\.openURL) private var openURL + + public init() {} + public var body: some View { + FormView { + Section { + EmptyView() + } footer: { + Text("**If I’ve defended your modern life, please consider sponsoring me.**") + .frame(maxWidth: .infinity, alignment: .leading) + } + + FormButton("GitHub Sponsor (recommended)") { + openURL(URL(string: "https://github.com/sponsors/nekohasekai")!) + } + FormButton("Other methods") { + openURL(URL(string: "https://sekai.icu/sponsors/")!) + } + } + .navigationTitle("Sponsors") + #if os(iOS) + .navigationBarTitleDisplayMode(.inline) + #endif + } +} diff --git a/Extension/Extension.entitlements b/Extension/Extension.entitlements new file mode 100644 index 0000000000000000000000000000000000000000..e2b5da861f09aee9178139da95042db18e60539f --- /dev/null +++ b/Extension/Extension.entitlements @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.developer.icloud-container-identifiers</key> + <array> + <string>iCloud.io.nekohasekai.sfabeino</string> + </array> + <key>com.apple.developer.icloud-services</key> + <array> + <string>CloudDocuments</string> + </array> + <key>com.apple.developer.networking.networkextension</key> + <array> + <string>packet-tunnel-provider</string> + </array> + <key>com.apple.developer.networking.wifi-info</key> + <true/> + <key>com.apple.developer.ubiquity-container-identifiers</key> + <array> + <string>iCloud.io.nekohasekai.sfabeino</string> + </array> + <key>com.apple.security.app-sandbox</key> + <true/> + <key>com.apple.security.application-groups</key> + <array> + <string>group.io.nekohasekai.sfabeino</string> + </array> + <key>com.apple.security.network.client</key> + <true/> + <key>com.apple.security.network.server</key> + <true/> +</dict> +</plist> diff --git a/Extension/Info.plist b/Extension/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..3059459e1a7d555b034fdd9d435b8210aaa2c7a6 --- /dev/null +++ b/Extension/Info.plist @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>NSExtension</key> + <dict> + <key>NSExtensionPointIdentifier</key> + <string>com.apple.networkextension.packet-tunnel</string> + <key>NSExtensionPrincipalClass</key> + <string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string> + </dict> +</dict> +</plist> diff --git a/Extension/PacketTunnelProvider.swift b/Extension/PacketTunnelProvider.swift new file mode 100644 index 0000000000000000000000000000000000000000..fbeef8d7e8d6bcb5aa6390ee9fd0015765754d36 --- /dev/null +++ b/Extension/PacketTunnelProvider.swift @@ -0,0 +1,4 @@ +import Foundation +import Library + +class PacketTunnelProvider: ExtensionProvider {} diff --git a/IntentsExtension/Info.plist b/IntentsExtension/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..8d15acbedf902db7cb324d94085826755da15a44 --- /dev/null +++ b/IntentsExtension/Info.plist @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>EXAppExtensionAttributes</key> + <dict> + <key>EXExtensionPointIdentifier</key> + <string>com.apple.appintents-extension</string> + </dict> +</dict> +</plist> diff --git a/IntentsExtension/Intents.swift b/IntentsExtension/Intents.swift new file mode 100644 index 0000000000000000000000000000000000000000..7f00471914dd206185a31966074bd29594085e2d --- /dev/null +++ b/IntentsExtension/Intents.swift @@ -0,0 +1,198 @@ +import AppIntents +import Foundation +import Libbox +import Library + +struct StartServiceIntent: AppIntent { + public static var title: LocalizedStringResource = "Start sing-box" + + static var description = + IntentDescription("Start or reload sing-box servie with specified profile") + + static var parameterSummary: some ParameterSummary { + Summary("Start sing-box service with profile \(\.$profile).") + } + + @Parameter(title: "Profile", optionsProvider: ProfileProvider()) + var profile: String + + func perform() async throws -> some IntentResult { + guard let extensionProfile = try await (ExtensionProfile.load()) else { + throw NSError(domain: "NetworkExtension not installed", code: 0) + } + let profileList = try await ProfileManager.list() + let specifiedProfile = profileList.first { $0.name == profile } + var profileChanged = false + if let specifiedProfile { + let specifiedProfileID = specifiedProfile.mustID + if await SharedPreferences.selectedProfileID.get() != specifiedProfileID { + await SharedPreferences.selectedProfileID.set(specifiedProfileID) + profileChanged = true + } + } else if profile != "default" { + throw NSError(domain: "Specified profile not found: \(profile)", code: 0) + } + if extensionProfile.status == .connected { + if !profileChanged { + return .result() + } + try LibboxNewStandaloneCommandClient()!.serviceReload() + } else if extensionProfile.status.isConnected { + try await extensionProfile.stop() + try await Task.sleep(nanoseconds: UInt64(100 * Double(NSEC_PER_MSEC))) + try await extensionProfile.start() + } else { + try await extensionProfile.start() + } + return .result() + } +} + +struct RestartServiceIntent: AppIntent { + static var title: LocalizedStringResource = "Restart sing-box" + + static var description = + IntentDescription("Restart sing-box service") + + static var parameterSummary: some ParameterSummary { + Summary("Restart sing-box service") + } + + func perform() async throws -> some IntentResult { + guard let extensionProfile = try await (ExtensionProfile.load()) else { + return .result() + } + if extensionProfile.status == .connected { + try LibboxNewStandaloneCommandClient()!.serviceReload() + } else if extensionProfile.status.isConnected { + try await extensionProfile.stop() + try await Task.sleep(nanoseconds: UInt64(100 * Double(NSEC_PER_MSEC))) + try await extensionProfile.start() + } else { + try await extensionProfile.start() + } + return .result() + } +} + +struct StopServiceIntent: AppIntent { + static var title: LocalizedStringResource = "Stop sing-box" + + static var description = + IntentDescription("Stop sing-box service") + + static var parameterSummary: some ParameterSummary { + Summary("Stop sing-box service") + } + + func perform() async throws -> some IntentResult { + guard let extensionProfile = try await (ExtensionProfile.load()) else { + return .result() + } + try await extensionProfile.stop() + return .result() + } +} + +struct ToggleServiceIntent: AppIntent { + static var title: LocalizedStringResource = "Toggle sing-box" + + static var description = + IntentDescription("Toggle sing-box service") + + static var parameterSummary: some ParameterSummary { + Summary("Toggle sing-box service") + } + + func perform() async throws -> some IntentResult & ReturnsValue<Bool> { + guard let extensionProfile = try await (ExtensionProfile.load()) else { + return .result(value: false) + } + if extensionProfile.status.isConnected { + try await extensionProfile.stop() + return .result(value: false) + + } else { + try await extensionProfile.start() + return .result(value: true) + } + } +} + +struct GetServiceStatus: AppIntent { + static var title: LocalizedStringResource = "Get is sing-box service started" + + static var description = + IntentDescription("Get is sing-box service started") + + static var parameterSummary: some ParameterSummary { + Summary("Get is sing-box service started") + } + + func perform() async throws -> some IntentResult & ReturnsValue<Bool> { + guard let extensionProfile = try await (ExtensionProfile.load()) else { + return .result(value: false) + } + return .result(value: extensionProfile.status.isConnected) + } +} + +struct GetCurrentProfile: AppIntent { + static var title: LocalizedStringResource = "Get current sing-box profile" + + static var description = + IntentDescription("Get current sing-box profile") + + static var parameterSummary: some ParameterSummary { + Summary("Get current sing-box profile") + } + + func perform() async throws -> some IntentResult & ReturnsValue<String> { + guard let profile = try await ProfileManager.get(SharedPreferences.selectedProfileID.get()) else { + throw NSError(domain: "No profile selected", code: 0) + } + return .result(value: profile.name) + } +} + +struct UpdateProfileIntent: AppIntent { + static var title: LocalizedStringResource = "Update sing-box profile" + + static var description = + IntentDescription("Update specified sing-box profile") + + static var parameterSummary: some ParameterSummary { + Summary("Update sing-box profile \(\.$profile).") + } + + @Parameter(title: "Profile", optionsProvider: RemoteProfileProvider()) + var profile: String + + init() {} + func perform() async throws -> some IntentResult { + guard let profile = try await ProfileManager.get(by: profile) else { + throw NSError(domain: "Specified profile not found: \(profile)", code: 0) + } + if profile.type != .remote { + throw NSError(domain: "Specified profile is not a remote profile", code: 0) + } + try await profile.updateRemoteProfile() + return .result() + } +} + +class ProfileProvider: DynamicOptionsProvider { + func results() async throws -> [String] { + var profileNames = try await ProfileManager.list().map(\.name) + if !profileNames.contains("default") { + profileNames.insert("default", at: 0) + } + return profileNames + } +} + +class RemoteProfileProvider: DynamicOptionsProvider { + func results() async throws -> [String] { + try await ProfileManager.listRemote().map(\.name) + } +} diff --git a/IntentsExtension/IntentsExtension.entitlements b/IntentsExtension/IntentsExtension.entitlements new file mode 100644 index 0000000000000000000000000000000000000000..4ea1b1c7febf3084a1f5c3cdab77277725eaa86f --- /dev/null +++ b/IntentsExtension/IntentsExtension.entitlements @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.security.app-sandbox</key> + <true/> + <key>com.apple.security.application-groups</key> + <array> + <string>group.io.nekohasekai.sfabeino</string> + </array> + <key>com.apple.security.network.client</key> + <true/> +</dict> +</plist> diff --git a/IntentsExtension/IntentsExtension.swift b/IntentsExtension/IntentsExtension.swift new file mode 100644 index 0000000000000000000000000000000000000000..40f0c0eb6f37b892b7de8fd0fcb2303e780fd350 --- /dev/null +++ b/IntentsExtension/IntentsExtension.swift @@ -0,0 +1,4 @@ +import AppIntents + +@main +struct IntentsExtension: AppIntentsExtension {} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..3e3e29e359d205307555aa41f19fb2a8415de885 --- /dev/null +++ b/LICENSE @@ -0,0 +1,14 @@ +Copyright (C) 2022 by nekohasekai <contact-sagernet@sekai.icu> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. \ No newline at end of file diff --git a/Libbox.xcframework/Info.plist b/Libbox.xcframework/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..18cdb4cdf66b92ac6678cf9ed3a296d8cd4a32fb --- /dev/null +++ b/Libbox.xcframework/Info.plist @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>AvailableLibraries</key> + <array> + <dict> + <key>LibraryIdentifier</key> + <string>ios-arm64</string> + <key>LibraryPath</key> + <string>Libbox.framework</string> + <key>SupportedArchitectures</key> + <array> + <string>arm64</string> + </array> + <key>SupportedPlatform</key> + <string>ios</string> + </dict> + <dict> + <key>LibraryIdentifier</key> + <string>ios-arm64_x86_64-simulator</string> + <key>LibraryPath</key> + <string>Libbox.framework</string> + <key>SupportedArchitectures</key> + <array> + <string>arm64</string> + <string>x86_64</string> + </array> + <key>SupportedPlatform</key> + <string>ios</string> + <key>SupportedPlatformVariant</key> + <string>simulator</string> + </dict> + <dict> + <key>LibraryIdentifier</key> + <string>tvos-arm64</string> + <key>LibraryPath</key> + <string>Libbox.framework</string> + <key>SupportedArchitectures</key> + <array> + <string>arm64</string> + </array> + <key>SupportedPlatform</key> + <string>tvos</string> + </dict> + <dict> + <key>LibraryIdentifier</key> + <string>macos-arm64_x86_64</string> + <key>LibraryPath</key> + <string>Libbox.framework</string> + <key>SupportedArchitectures</key> + <array> + <string>arm64</string> + <string>x86_64</string> + </array> + <key>SupportedPlatform</key> + <string>macos</string> + </dict> + <dict> + <key>LibraryIdentifier</key> + <string>tvos-arm64_x86_64-simulator</string> + <key>LibraryPath</key> + <string>Libbox.framework</string> + <key>SupportedArchitectures</key> + <array> + <string>arm64</string> + <string>x86_64</string> + </array> + <key>SupportedPlatform</key> + <string>tvos</string> + <key>SupportedPlatformVariant</key> + <string>simulator</string> + </dict> + </array> + <key>CFBundlePackageType</key> + <string>XFWK</string> + <key>XCFrameworkFormatVersion</key> + <string>1.0</string> +</dict> +</plist> diff --git a/Libbox.xcframework/ios-arm64/Libbox.framework/Headers b/Libbox.xcframework/ios-arm64/Libbox.framework/Headers new file mode 120000 index 0000000000000000000000000000000000000000..a177d2a6b92600696030834c319f5e1434f9ee6a --- /dev/null +++ b/Libbox.xcframework/ios-arm64/Libbox.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/Libbox.xcframework/ios-arm64/Libbox.framework/Libbox b/Libbox.xcframework/ios-arm64/Libbox.framework/Libbox new file mode 120000 index 0000000000000000000000000000000000000000..97ad87691145a5467708b9e044f9e0e4be79a763 --- /dev/null +++ b/Libbox.xcframework/ios-arm64/Libbox.framework/Libbox @@ -0,0 +1 @@ +Versions/Current/Libbox \ No newline at end of file diff --git a/Libbox.xcframework/ios-arm64/Libbox.framework/Modules b/Libbox.xcframework/ios-arm64/Libbox.framework/Modules new file mode 120000 index 0000000000000000000000000000000000000000..5736f3186e797b8b787748c9979d0fed3b0536c3 --- /dev/null +++ b/Libbox.xcframework/ios-arm64/Libbox.framework/Modules @@ -0,0 +1 @@ +Versions/Current/Modules \ No newline at end of file diff --git a/Libbox.xcframework/ios-arm64/Libbox.framework/Resources b/Libbox.xcframework/ios-arm64/Libbox.framework/Resources new file mode 120000 index 0000000000000000000000000000000000000000..953ee36f3bb709faf58a351e0b33c353e337c0a2 --- /dev/null +++ b/Libbox.xcframework/ios-arm64/Libbox.framework/Resources @@ -0,0 +1 @@ +Versions/Current/Resources \ No newline at end of file diff --git a/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Headers/Libbox.h b/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Headers/Libbox.h new file mode 100644 index 0000000000000000000000000000000000000000..7e3aff43ac0a1cd946c152e9547907ff842adc70 --- /dev/null +++ b/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Headers/Libbox.h @@ -0,0 +1,13 @@ + +// Objective-C API for talking to the following Go packages +// +// github.com/sagernet/sing-box/experimental/libbox +// +// File is generated by gomobile bind. Do not edit. +#ifndef __Libbox_FRAMEWORK_H__ +#define __Libbox_FRAMEWORK_H__ + +#include "Libbox.objc.h" +#include "Universe.objc.h" + +#endif diff --git a/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Headers/Libbox.objc.h b/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Headers/Libbox.objc.h new file mode 100644 index 0000000000000000000000000000000000000000..98f33b9c58db878543752eb7027cc746d69a46fe --- /dev/null +++ b/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Headers/Libbox.objc.h @@ -0,0 +1,811 @@ +// Objective-C API for talking to github.com/sagernet/sing-box/experimental/libbox Go package. +// gobind -lang=objc github.com/sagernet/sing-box/experimental/libbox +// +// File is generated by gobind. Do not edit. + +#ifndef __Libbox_H__ +#define __Libbox_H__ + +@import Foundation; +#include "ref.h" +#include "Universe.objc.h" + + +@class LibboxBoxService; +@class LibboxCommandClient; +@class LibboxCommandClientOptions; +@class LibboxCommandServer; +@class LibboxErrorMessage; +@class LibboxExchangeContext; +@class LibboxImportRemoteProfile; +@class LibboxNetworkInterface; +@class LibboxOutboundGroup; +@class LibboxOutboundGroupItem; +@class LibboxPProfServer; +@class LibboxProfileContent; +@class LibboxProfileContentRequest; +@class LibboxProfileDecoder; +@class LibboxProfileEncoder; +@class LibboxProfilePreview; +@class LibboxRoutePrefix; +@class LibboxStatusMessage; +@class LibboxSystemProxyStatus; +@class LibboxWIFIState; +@protocol LibboxCommandClientHandler; +@class LibboxCommandClientHandler; +@protocol LibboxCommandServerHandler; +@class LibboxCommandServerHandler; +@protocol LibboxFunc; +@class LibboxFunc; +@protocol LibboxHTTPClient; +@class LibboxHTTPClient; +@protocol LibboxHTTPRequest; +@class LibboxHTTPRequest; +@protocol LibboxHTTPResponse; +@class LibboxHTTPResponse; +@protocol LibboxInterfaceUpdateListener; +@class LibboxInterfaceUpdateListener; +@protocol LibboxLocalDNSTransport; +@class LibboxLocalDNSTransport; +@protocol LibboxNetworkInterfaceIterator; +@class LibboxNetworkInterfaceIterator; +@protocol LibboxOnDemandRule; +@class LibboxOnDemandRule; +@protocol LibboxOnDemandRuleIterator; +@class LibboxOnDemandRuleIterator; +@protocol LibboxOutboundGroupItemIterator; +@class LibboxOutboundGroupItemIterator; +@protocol LibboxOutboundGroupIterator; +@class LibboxOutboundGroupIterator; +@protocol LibboxPlatformInterface; +@class LibboxPlatformInterface; +@protocol LibboxProfilePreviewIterator; +@class LibboxProfilePreviewIterator; +@protocol LibboxRoutePrefixIterator; +@class LibboxRoutePrefixIterator; +@protocol LibboxStringIterator; +@class LibboxStringIterator; +@protocol LibboxTunInterface; +@class LibboxTunInterface; +@protocol LibboxTunOptions; +@class LibboxTunOptions; + +@protocol LibboxCommandClientHandler <NSObject> +- (void)clearLog; +- (void)connected; +- (void)disconnected:(NSString* _Nullable)message; +- (void)initializeClashMode:(id<LibboxStringIterator> _Nullable)modeList currentMode:(NSString* _Nullable)currentMode; +- (void)updateClashMode:(NSString* _Nullable)newMode; +- (void)writeGroups:(id<LibboxOutboundGroupIterator> _Nullable)message; +- (void)writeLog:(NSString* _Nullable)message; +- (void)writeStatus:(LibboxStatusMessage* _Nullable)message; +@end + +@protocol LibboxCommandServerHandler <NSObject> +- (LibboxSystemProxyStatus* _Nullable)getSystemProxyStatus; +- (void)postServiceClose; +- (BOOL)serviceReload:(NSError* _Nullable* _Nullable)error; +- (BOOL)setSystemProxyEnabled:(BOOL)isEnabled error:(NSError* _Nullable* _Nullable)error; +@end + +@protocol LibboxFunc <NSObject> +- (BOOL)invoke:(NSError* _Nullable* _Nullable)error; +@end + +@protocol LibboxHTTPClient <NSObject> +- (void)close; +- (void)keepAlive; +- (void)modernTLS; +- (id<LibboxHTTPRequest> _Nullable)newRequest; +- (void)pinnedSHA256:(NSString* _Nullable)sumHex; +- (void)pinnedTLS12; +- (void)restrictedTLS; +- (void)trySocks5:(int32_t)port; +@end + +@protocol LibboxHTTPRequest <NSObject> +- (id<LibboxHTTPResponse> _Nullable)execute:(NSError* _Nullable* _Nullable)error; +- (void)randomUserAgent; +- (void)setContent:(NSData* _Nullable)content; +- (void)setContentString:(NSString* _Nullable)content; +- (void)setHeader:(NSString* _Nullable)key value:(NSString* _Nullable)value; +- (void)setMethod:(NSString* _Nullable)method; +- (BOOL)setURL:(NSString* _Nullable)link error:(NSError* _Nullable* _Nullable)error; +- (void)setUserAgent:(NSString* _Nullable)userAgent; +@end + +@protocol LibboxHTTPResponse <NSObject> +- (NSData* _Nullable)getContent:(NSError* _Nullable* _Nullable)error; +- (NSString* _Nonnull)getContentString:(NSError* _Nullable* _Nullable)error; +- (BOOL)writeTo:(NSString* _Nullable)path error:(NSError* _Nullable* _Nullable)error; +@end + +@protocol LibboxInterfaceUpdateListener <NSObject> +- (void)updateDefaultInterface:(NSString* _Nullable)interfaceName interfaceIndex:(int32_t)interfaceIndex; +@end + +@protocol LibboxLocalDNSTransport <NSObject> +- (BOOL)exchange:(LibboxExchangeContext* _Nullable)ctx message:(NSData* _Nullable)message error:(NSError* _Nullable* _Nullable)error; +- (BOOL)lookup:(LibboxExchangeContext* _Nullable)ctx network:(NSString* _Nullable)network domain:(NSString* _Nullable)domain error:(NSError* _Nullable* _Nullable)error; +- (BOOL)raw; +@end + +@protocol LibboxNetworkInterfaceIterator <NSObject> +- (BOOL)hasNext; +- (LibboxNetworkInterface* _Nullable)next; +@end + +@protocol LibboxOnDemandRule <NSObject> +- (id<LibboxStringIterator> _Nullable)dnsSearchDomainMatch; +- (id<LibboxStringIterator> _Nullable)dnsServerAddressMatch; +- (int32_t)interfaceTypeMatch; +- (NSString* _Nonnull)probeURL; +- (id<LibboxStringIterator> _Nullable)ssidMatch; +- (int32_t)target; +@end + +@protocol LibboxOnDemandRuleIterator <NSObject> +- (BOOL)hasNext; +- (id<LibboxOnDemandRule> _Nullable)next; +@end + +@protocol LibboxOutboundGroupItemIterator <NSObject> +- (BOOL)hasNext; +- (LibboxOutboundGroupItem* _Nullable)next; +@end + +@protocol LibboxOutboundGroupIterator <NSObject> +- (BOOL)hasNext; +- (LibboxOutboundGroup* _Nullable)next; +@end + +@protocol LibboxPlatformInterface <NSObject> +- (BOOL)autoDetectInterfaceControl:(int32_t)fd error:(NSError* _Nullable* _Nullable)error; +- (void)clearDNSCache; +- (BOOL)closeDefaultInterfaceMonitor:(id<LibboxInterfaceUpdateListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; +- (BOOL)findConnectionOwner:(int32_t)ipProtocol sourceAddress:(NSString* _Nullable)sourceAddress sourcePort:(int32_t)sourcePort destinationAddress:(NSString* _Nullable)destinationAddress destinationPort:(int32_t)destinationPort ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (id<LibboxNetworkInterfaceIterator> _Nullable)getInterfaces:(NSError* _Nullable* _Nullable)error; +- (BOOL)includeAllNetworks; +- (BOOL)openTun:(id<LibboxTunOptions> _Nullable)options ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (NSString* _Nonnull)packageNameByUid:(int32_t)uid error:(NSError* _Nullable* _Nullable)error; +- (LibboxWIFIState* _Nullable)readWIFIState; +- (BOOL)startDefaultInterfaceMonitor:(id<LibboxInterfaceUpdateListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; +- (BOOL)uidByPackageName:(NSString* _Nullable)packageName ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (BOOL)underNetworkExtension; +- (BOOL)usePlatformAutoDetectInterfaceControl; +- (BOOL)usePlatformDefaultInterfaceMonitor; +- (BOOL)usePlatformInterfaceGetter; +- (BOOL)useProcFS; +- (void)writeLog:(NSString* _Nullable)message; +@end + +@protocol LibboxProfilePreviewIterator <NSObject> +- (BOOL)hasNext; +- (LibboxProfilePreview* _Nullable)next; +@end + +@protocol LibboxRoutePrefixIterator <NSObject> +- (BOOL)hasNext; +- (LibboxRoutePrefix* _Nullable)next; +@end + +@protocol LibboxStringIterator <NSObject> +- (BOOL)hasNext; +- (NSString* _Nonnull)next; +@end + +@protocol LibboxTunInterface <NSObject> +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (int32_t)fileDescriptor; +@end + +@protocol LibboxTunOptions <NSObject> +- (BOOL)getAutoRoute; +- (NSString* _Nonnull)getDNSServerAddress:(NSError* _Nullable* _Nullable)error; +- (id<LibboxStringIterator> _Nullable)getExcludePackage; +- (id<LibboxStringIterator> _Nullable)getHTTPProxyBypassDomain; +- (id<LibboxStringIterator> _Nullable)getHTTPProxyMatchDomain; +- (NSString* _Nonnull)getHTTPProxyServer; +- (int32_t)getHTTPProxyServerPort; +- (id<LibboxStringIterator> _Nullable)getIncludePackage; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4Address; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteExcludeAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteRange; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6Address; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteExcludeAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteRange; +- (int32_t)getMTU; +- (BOOL)getStrictRoute; +- (BOOL)isHTTPProxyEnabled; +@end + +@interface LibboxBoxService : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (BOOL)needWIFIState; +- (void)pause; +- (BOOL)start:(NSError* _Nullable* _Nullable)error; +- (void)wake; +@end + +@interface LibboxCommandClient : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nullable instancetype)init:(id<LibboxCommandClientHandler> _Nullable)handler options:(LibboxCommandClientOptions* _Nullable)options; +- (BOOL)closeConnections:(NSError* _Nullable* _Nullable)error; +- (BOOL)connect:(NSError* _Nullable* _Nullable)error; +- (BOOL)disconnect:(NSError* _Nullable* _Nullable)error; +- (LibboxSystemProxyStatus* _Nullable)getSystemProxyStatus:(NSError* _Nullable* _Nullable)error; +- (BOOL)selectOutbound:(NSString* _Nullable)groupTag outboundTag:(NSString* _Nullable)outboundTag error:(NSError* _Nullable* _Nullable)error; +- (BOOL)serviceClose:(NSError* _Nullable* _Nullable)error; +- (BOOL)serviceReload:(NSError* _Nullable* _Nullable)error; +- (BOOL)setClashMode:(NSString* _Nullable)newMode error:(NSError* _Nullable* _Nullable)error; +- (BOOL)setGroupExpand:(NSString* _Nullable)groupTag isExpand:(BOOL)isExpand error:(NSError* _Nullable* _Nullable)error; +- (BOOL)setSystemProxyEnabled:(BOOL)isEnabled error:(NSError* _Nullable* _Nullable)error; +- (BOOL)urlTest:(NSString* _Nullable)groupTag error:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxCommandClientOptions : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int32_t command; +@property (nonatomic) int64_t statusInterval; +@end + +@interface LibboxCommandServer : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nullable instancetype)init:(id<LibboxCommandServerHandler> _Nullable)handler maxLines:(int32_t)maxLines; +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (void)resetLog; +- (void)setService:(LibboxBoxService* _Nullable)newService; +- (BOOL)start:(NSError* _Nullable* _Nullable)error; +- (void)writeMessage:(NSString* _Nullable)message; +@end + +@interface LibboxErrorMessage : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull message; +- (NSData* _Nullable)encode; +@end + +@interface LibboxExchangeContext : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (void)errnoCode:(int32_t)code; +- (void)errorCode:(int32_t)code; +- (void)onCancel:(id<LibboxFunc> _Nullable)callback; +- (void)rawSuccess:(NSData* _Nullable)result; +- (void)success:(NSString* _Nullable)result; +@end + +@interface LibboxImportRemoteProfile : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull name; +@property (nonatomic) NSString* _Nonnull url; +@property (nonatomic) NSString* _Nonnull host; +@end + +@interface LibboxNetworkInterface : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int32_t index; +@property (nonatomic) int32_t mtu; +@property (nonatomic) NSString* _Nonnull name; +@property (nonatomic) id<LibboxStringIterator> _Nullable addresses; +@end + +@interface LibboxOutboundGroup : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull tag; +@property (nonatomic) NSString* _Nonnull type; +@property (nonatomic) BOOL selectable; +@property (nonatomic) NSString* _Nonnull selected; +@property (nonatomic) BOOL isExpand; +- (id<LibboxOutboundGroupItemIterator> _Nullable)getItems; +@end + +@interface LibboxOutboundGroupItem : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull tag; +@property (nonatomic) NSString* _Nonnull type; +@property (nonatomic) int64_t urlTestTime; +@property (nonatomic) int32_t urlTestDelay; +@end + +@interface LibboxPProfServer : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nullable instancetype)init:(long)port; +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (BOOL)start:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxProfileContent : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull name; +@property (nonatomic) int32_t type; +@property (nonatomic) NSString* _Nonnull config; +@property (nonatomic) NSString* _Nonnull remotePath; +@property (nonatomic) BOOL autoUpdate; +@property (nonatomic) int32_t autoUpdateInterval; +@property (nonatomic) int64_t lastUpdated; +- (NSData* _Nullable)encode; +@end + +@interface LibboxProfileContentRequest : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int64_t profileID; +- (NSData* _Nullable)encode; +@end + +@interface LibboxProfileDecoder : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (BOOL)decode:(NSData* _Nullable)data error:(NSError* _Nullable* _Nullable)error; +- (id<LibboxProfilePreviewIterator> _Nullable)iterator; +@end + +@interface LibboxProfileEncoder : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (void)append:(LibboxProfilePreview* _Nullable)profile; +- (NSData* _Nullable)encode; +@end + +@interface LibboxProfilePreview : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int64_t profileID; +@property (nonatomic) NSString* _Nonnull name; +@property (nonatomic) int32_t type; +@end + +@interface LibboxRoutePrefix : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (NSString* _Nonnull)address; +- (NSString* _Nonnull)mask; +- (int32_t)prefix; +- (NSString* _Nonnull)string; +@end + +@interface LibboxStatusMessage : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int64_t memory; +@property (nonatomic) int32_t goroutines; +@property (nonatomic) int32_t connectionsIn; +@property (nonatomic) int32_t connectionsOut; +@property (nonatomic) BOOL trafficAvailable; +@property (nonatomic) int64_t uplink; +@property (nonatomic) int64_t downlink; +@property (nonatomic) int64_t uplinkTotal; +@property (nonatomic) int64_t downlinkTotal; +@end + +@interface LibboxSystemProxyStatus : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) BOOL available; +@property (nonatomic) BOOL enabled; +@end + +@interface LibboxWIFIState : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nullable instancetype)init:(NSString* _Nullable)wifiSSID wifiBSSID:(NSString* _Nullable)wifiBSSID; +@property (nonatomic) NSString* _Nonnull ssid; +@property (nonatomic) NSString* _Nonnull bssid; +@end + +FOUNDATION_EXPORT const int32_t LibboxCommandClashMode; +FOUNDATION_EXPORT const int32_t LibboxCommandCloseConnections; +FOUNDATION_EXPORT const int32_t LibboxCommandGetSystemProxyStatus; +FOUNDATION_EXPORT const int32_t LibboxCommandGroup; +FOUNDATION_EXPORT const int32_t LibboxCommandGroupExpand; +FOUNDATION_EXPORT const int32_t LibboxCommandLog; +FOUNDATION_EXPORT const int32_t LibboxCommandSelectOutbound; +FOUNDATION_EXPORT const int32_t LibboxCommandServiceClose; +FOUNDATION_EXPORT const int32_t LibboxCommandServiceReload; +FOUNDATION_EXPORT const int32_t LibboxCommandSetClashMode; +FOUNDATION_EXPORT const int32_t LibboxCommandSetSystemProxyEnabled; +FOUNDATION_EXPORT const int32_t LibboxCommandStatus; +FOUNDATION_EXPORT const int32_t LibboxCommandURLTest; +FOUNDATION_EXPORT const int64_t LibboxMessageTypeError; +FOUNDATION_EXPORT const int64_t LibboxMessageTypeProfileContent; +FOUNDATION_EXPORT const int64_t LibboxMessageTypeProfileContentRequest; +FOUNDATION_EXPORT const int64_t LibboxMessageTypeProfileList; +FOUNDATION_EXPORT const int32_t LibboxProfileTypeLocal; +FOUNDATION_EXPORT const int32_t LibboxProfileTypeRemote; +FOUNDATION_EXPORT const int32_t LibboxProfileTypeiCloud; + +FOUNDATION_EXPORT BOOL LibboxCheckConfig(NSString* _Nullable configContent, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT void LibboxClearServiceError(void); + +FOUNDATION_EXPORT LibboxErrorMessage* _Nullable LibboxDecodeErrorMessage(NSData* _Nullable data, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT int32_t LibboxDecodeLengthChunk(NSData* _Nullable data); + +FOUNDATION_EXPORT LibboxProfileContent* _Nullable LibboxDecodeProfileContent(NSData* _Nullable data, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT LibboxProfileContentRequest* _Nullable LibboxDecodeProfileContentRequest(NSData* _Nullable data, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT NSData* _Nullable LibboxEncodeChunkedMessage(NSData* _Nullable data); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxFormatBytes(int64_t length); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxFormatConfig(NSString* _Nullable configContent, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxFormatMemoryBytes(int64_t length); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxGenerateRemoteProfileImportLink(NSString* _Nullable name, NSString* _Nullable remoteURL); + +FOUNDATION_EXPORT int32_t LibboxGetTunnelFileDescriptor(void); + +FOUNDATION_EXPORT LibboxCommandClient* _Nullable LibboxNewCommandClient(id<LibboxCommandClientHandler> _Nullable handler, LibboxCommandClientOptions* _Nullable options); + +FOUNDATION_EXPORT LibboxCommandServer* _Nullable LibboxNewCommandServer(id<LibboxCommandServerHandler> _Nullable handler, int32_t maxLines); + +FOUNDATION_EXPORT id<LibboxHTTPClient> _Nullable LibboxNewHTTPClient(void); + +FOUNDATION_EXPORT LibboxPProfServer* _Nullable LibboxNewPProfServer(long port); + +FOUNDATION_EXPORT LibboxBoxService* _Nullable LibboxNewService(NSString* _Nullable configContent, id<LibboxPlatformInterface> _Nullable platformInterface, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT LibboxCommandClient* _Nullable LibboxNewStandaloneCommandClient(void); + +FOUNDATION_EXPORT LibboxWIFIState* _Nullable LibboxNewWIFIState(NSString* _Nullable wifiSSID, NSString* _Nullable wifiBSSID); + +FOUNDATION_EXPORT LibboxImportRemoteProfile* _Nullable LibboxParseRemoteProfileImportLink(NSString* _Nullable importLink, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxProxyDisplayType(NSString* _Nullable proxyType); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxReadServiceError(NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT BOOL LibboxRedirectStderr(NSString* _Nullable path, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT void LibboxRegisterLocalDNSTransport(id<LibboxLocalDNSTransport> _Nullable transport); + +FOUNDATION_EXPORT void LibboxSetMemoryLimit(BOOL enabled); + +FOUNDATION_EXPORT void LibboxSetup(NSString* _Nullable basePath, NSString* _Nullable workingPath, NSString* _Nullable tempPath, BOOL isTVOS); + +FOUNDATION_EXPORT BOOL LibboxSetupWithUsername(NSString* _Nullable basePath, NSString* _Nullable workingPath, NSString* _Nullable tempPath, NSString* _Nullable username, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxVersion(void); + +FOUNDATION_EXPORT BOOL LibboxWriteServiceError(NSString* _Nullable message, NSError* _Nullable* _Nullable error); + +@class LibboxCommandClientHandler; + +@class LibboxCommandServerHandler; + +@class LibboxFunc; + +@class LibboxHTTPClient; + +@class LibboxHTTPRequest; + +@class LibboxHTTPResponse; + +@class LibboxInterfaceUpdateListener; + +@class LibboxLocalDNSTransport; + +@class LibboxNetworkInterfaceIterator; + +@class LibboxOnDemandRule; + +@class LibboxOnDemandRuleIterator; + +@class LibboxOutboundGroupItemIterator; + +@class LibboxOutboundGroupIterator; + +@class LibboxPlatformInterface; + +@class LibboxProfilePreviewIterator; + +@class LibboxRoutePrefixIterator; + +@class LibboxStringIterator; + +@class LibboxTunInterface; + +@class LibboxTunOptions; + +@interface LibboxCommandClientHandler : NSObject <goSeqRefInterface, LibboxCommandClientHandler> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (void)clearLog; +- (void)connected; +- (void)disconnected:(NSString* _Nullable)message; +- (void)initializeClashMode:(id<LibboxStringIterator> _Nullable)modeList currentMode:(NSString* _Nullable)currentMode; +- (void)updateClashMode:(NSString* _Nullable)newMode; +- (void)writeGroups:(id<LibboxOutboundGroupIterator> _Nullable)message; +- (void)writeLog:(NSString* _Nullable)message; +- (void)writeStatus:(LibboxStatusMessage* _Nullable)message; +@end + +@interface LibboxCommandServerHandler : NSObject <goSeqRefInterface, LibboxCommandServerHandler> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (LibboxSystemProxyStatus* _Nullable)getSystemProxyStatus; +- (void)postServiceClose; +- (BOOL)serviceReload:(NSError* _Nullable* _Nullable)error; +- (BOOL)setSystemProxyEnabled:(BOOL)isEnabled error:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxFunc : NSObject <goSeqRefInterface, LibboxFunc> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)invoke:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxHTTPClient : NSObject <goSeqRefInterface, LibboxHTTPClient> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (void)close; +- (void)keepAlive; +- (void)modernTLS; +- (id<LibboxHTTPRequest> _Nullable)newRequest; +- (void)pinnedSHA256:(NSString* _Nullable)sumHex; +- (void)pinnedTLS12; +- (void)restrictedTLS; +- (void)trySocks5:(int32_t)port; +@end + +@interface LibboxHTTPRequest : NSObject <goSeqRefInterface, LibboxHTTPRequest> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (id<LibboxHTTPResponse> _Nullable)execute:(NSError* _Nullable* _Nullable)error; +- (void)randomUserAgent; +- (void)setContent:(NSData* _Nullable)content; +- (void)setContentString:(NSString* _Nullable)content; +- (void)setHeader:(NSString* _Nullable)key value:(NSString* _Nullable)value; +- (void)setMethod:(NSString* _Nullable)method; +- (BOOL)setURL:(NSString* _Nullable)link error:(NSError* _Nullable* _Nullable)error; +- (void)setUserAgent:(NSString* _Nullable)userAgent; +@end + +@interface LibboxHTTPResponse : NSObject <goSeqRefInterface, LibboxHTTPResponse> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (NSData* _Nullable)getContent:(NSError* _Nullable* _Nullable)error; +- (NSString* _Nonnull)getContentString:(NSError* _Nullable* _Nullable)error; +- (BOOL)writeTo:(NSString* _Nullable)path error:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxInterfaceUpdateListener : NSObject <goSeqRefInterface, LibboxInterfaceUpdateListener> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (void)updateDefaultInterface:(NSString* _Nullable)interfaceName interfaceIndex:(int32_t)interfaceIndex; +@end + +@interface LibboxLocalDNSTransport : NSObject <goSeqRefInterface, LibboxLocalDNSTransport> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)exchange:(LibboxExchangeContext* _Nullable)ctx message:(NSData* _Nullable)message error:(NSError* _Nullable* _Nullable)error; +- (BOOL)lookup:(LibboxExchangeContext* _Nullable)ctx network:(NSString* _Nullable)network domain:(NSString* _Nullable)domain error:(NSError* _Nullable* _Nullable)error; +- (BOOL)raw; +@end + +@interface LibboxNetworkInterfaceIterator : NSObject <goSeqRefInterface, LibboxNetworkInterfaceIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxNetworkInterface* _Nullable)next; +@end + +@interface LibboxOnDemandRule : NSObject <goSeqRefInterface, LibboxOnDemandRule> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (id<LibboxStringIterator> _Nullable)dnsSearchDomainMatch; +- (id<LibboxStringIterator> _Nullable)dnsServerAddressMatch; +- (int32_t)interfaceTypeMatch; +- (NSString* _Nonnull)probeURL; +- (id<LibboxStringIterator> _Nullable)ssidMatch; +- (int32_t)target; +@end + +@interface LibboxOnDemandRuleIterator : NSObject <goSeqRefInterface, LibboxOnDemandRuleIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (id<LibboxOnDemandRule> _Nullable)next; +@end + +@interface LibboxOutboundGroupItemIterator : NSObject <goSeqRefInterface, LibboxOutboundGroupItemIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxOutboundGroupItem* _Nullable)next; +@end + +@interface LibboxOutboundGroupIterator : NSObject <goSeqRefInterface, LibboxOutboundGroupIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxOutboundGroup* _Nullable)next; +@end + +@interface LibboxPlatformInterface : NSObject <goSeqRefInterface, LibboxPlatformInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)autoDetectInterfaceControl:(int32_t)fd error:(NSError* _Nullable* _Nullable)error; +- (void)clearDNSCache; +- (BOOL)closeDefaultInterfaceMonitor:(id<LibboxInterfaceUpdateListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; +- (BOOL)findConnectionOwner:(int32_t)ipProtocol sourceAddress:(NSString* _Nullable)sourceAddress sourcePort:(int32_t)sourcePort destinationAddress:(NSString* _Nullable)destinationAddress destinationPort:(int32_t)destinationPort ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (id<LibboxNetworkInterfaceIterator> _Nullable)getInterfaces:(NSError* _Nullable* _Nullable)error; +- (BOOL)includeAllNetworks; +- (BOOL)openTun:(id<LibboxTunOptions> _Nullable)options ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (NSString* _Nonnull)packageNameByUid:(int32_t)uid error:(NSError* _Nullable* _Nullable)error; +- (LibboxWIFIState* _Nullable)readWIFIState; +- (BOOL)startDefaultInterfaceMonitor:(id<LibboxInterfaceUpdateListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; +- (BOOL)uidByPackageName:(NSString* _Nullable)packageName ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (BOOL)underNetworkExtension; +- (BOOL)usePlatformAutoDetectInterfaceControl; +- (BOOL)usePlatformDefaultInterfaceMonitor; +- (BOOL)usePlatformInterfaceGetter; +- (BOOL)useProcFS; +- (void)writeLog:(NSString* _Nullable)message; +@end + +@interface LibboxProfilePreviewIterator : NSObject <goSeqRefInterface, LibboxProfilePreviewIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxProfilePreview* _Nullable)next; +@end + +@interface LibboxRoutePrefixIterator : NSObject <goSeqRefInterface, LibboxRoutePrefixIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxRoutePrefix* _Nullable)next; +@end + +@interface LibboxStringIterator : NSObject <goSeqRefInterface, LibboxStringIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (NSString* _Nonnull)next; +@end + +@interface LibboxTunInterface : NSObject <goSeqRefInterface, LibboxTunInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (int32_t)fileDescriptor; +@end + +@interface LibboxTunOptions : NSObject <goSeqRefInterface, LibboxTunOptions> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)getAutoRoute; +- (NSString* _Nonnull)getDNSServerAddress:(NSError* _Nullable* _Nullable)error; +- (id<LibboxStringIterator> _Nullable)getExcludePackage; +- (id<LibboxStringIterator> _Nullable)getHTTPProxyBypassDomain; +- (id<LibboxStringIterator> _Nullable)getHTTPProxyMatchDomain; +- (NSString* _Nonnull)getHTTPProxyServer; +- (int32_t)getHTTPProxyServerPort; +- (id<LibboxStringIterator> _Nullable)getIncludePackage; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4Address; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteExcludeAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteRange; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6Address; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteExcludeAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteRange; +- (int32_t)getMTU; +- (BOOL)getStrictRoute; +- (BOOL)isHTTPProxyEnabled; +@end + +#endif diff --git a/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Headers/Universe.objc.h b/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Headers/Universe.objc.h new file mode 100644 index 0000000000000000000000000000000000000000..019e7502d581983722a15bf30799e85cbc5dd766 --- /dev/null +++ b/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Headers/Universe.objc.h @@ -0,0 +1,29 @@ +// Objective-C API for talking to Go package. +// gobind -lang=objc +// +// File is generated by gobind. Do not edit. + +#ifndef __Universe_H__ +#define __Universe_H__ + +@import Foundation; +#include "ref.h" + +@protocol Universeerror; +@class Universeerror; + +@protocol Universeerror <NSObject> +- (NSString* _Nonnull)error; +@end + +@class Universeerror; + +@interface Universeerror : NSError <goSeqRefInterface, Universeerror> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (NSString* _Nonnull)error; +@end + +#endif diff --git a/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Headers/ref.h b/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Headers/ref.h new file mode 100644 index 0000000000000000000000000000000000000000..b8036a4d85c7387f3def61473a071b5d8c4c8208 --- /dev/null +++ b/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Headers/ref.h @@ -0,0 +1,35 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef __GO_REF_HDR__ +#define __GO_REF_HDR__ + +#include <Foundation/Foundation.h> + +// GoSeqRef is an object tagged with an integer for passing back and +// forth across the language boundary. A GoSeqRef may represent either +// an instance of a Go object, or an Objective-C object passed to Go. +// The explicit allocation of a GoSeqRef is used to pin a Go object +// when it is passed to Objective-C. The Go seq package maintains a +// reference to the Go object in a map keyed by the refnum along with +// a reference count. When the reference count reaches zero, the Go +// seq package will clear the corresponding entry in the map. +@interface GoSeqRef : NSObject { +} +@property(readonly) int32_t refnum; +@property(strong) id obj; // NULL when representing a Go object. + +// new GoSeqRef object to proxy a Go object. The refnum must be +// provided from Go side. +- (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj; + +- (int32_t)incNum; + +@end + +@protocol goSeqRefInterface +-(GoSeqRef*) _ref; +@end + +#endif diff --git a/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Libbox b/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Libbox new file mode 100644 index 0000000000000000000000000000000000000000..783e0cf1cae07895c86649008b77bdc94de94ffe Binary files /dev/null and b/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Libbox differ diff --git a/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Modules/module.modulemap b/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Modules/module.modulemap new file mode 100644 index 0000000000000000000000000000000000000000..1ff5e29bdfd41d06429a16857384ad8cbffb85fe --- /dev/null +++ b/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Modules/module.modulemap @@ -0,0 +1,8 @@ +framework module "Libbox" { + header "ref.h" + header "Libbox.objc.h" + header "Universe.objc.h" + header "Libbox.h" + + export * +} \ No newline at end of file diff --git a/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Resources/Info.plist b/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Resources/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..0d1a4b8ab9b1fc8e9357197398f73353470cb636 --- /dev/null +++ b/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/A/Resources/Info.plist @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> + <plist version="1.0"> + <dict> + </dict> + </plist> diff --git a/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/Current b/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/Current new file mode 120000 index 0000000000000000000000000000000000000000..8c7e5a667f1b771847fe88c01c3de34413a1b220 --- /dev/null +++ b/Libbox.xcframework/ios-arm64/Libbox.framework/Versions/Current @@ -0,0 +1 @@ +A \ No newline at end of file diff --git a/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Headers b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Headers new file mode 120000 index 0000000000000000000000000000000000000000..a177d2a6b92600696030834c319f5e1434f9ee6a --- /dev/null +++ b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Libbox b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Libbox new file mode 120000 index 0000000000000000000000000000000000000000..97ad87691145a5467708b9e044f9e0e4be79a763 --- /dev/null +++ b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Libbox @@ -0,0 +1 @@ +Versions/Current/Libbox \ No newline at end of file diff --git a/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Modules b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Modules new file mode 120000 index 0000000000000000000000000000000000000000..5736f3186e797b8b787748c9979d0fed3b0536c3 --- /dev/null +++ b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Modules @@ -0,0 +1 @@ +Versions/Current/Modules \ No newline at end of file diff --git a/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Resources b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Resources new file mode 120000 index 0000000000000000000000000000000000000000..953ee36f3bb709faf58a351e0b33c353e337c0a2 --- /dev/null +++ b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Resources @@ -0,0 +1 @@ +Versions/Current/Resources \ No newline at end of file diff --git a/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/Libbox.h b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/Libbox.h new file mode 100644 index 0000000000000000000000000000000000000000..7e3aff43ac0a1cd946c152e9547907ff842adc70 --- /dev/null +++ b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/Libbox.h @@ -0,0 +1,13 @@ + +// Objective-C API for talking to the following Go packages +// +// github.com/sagernet/sing-box/experimental/libbox +// +// File is generated by gomobile bind. Do not edit. +#ifndef __Libbox_FRAMEWORK_H__ +#define __Libbox_FRAMEWORK_H__ + +#include "Libbox.objc.h" +#include "Universe.objc.h" + +#endif diff --git a/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/Libbox.objc.h b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/Libbox.objc.h new file mode 100644 index 0000000000000000000000000000000000000000..98f33b9c58db878543752eb7027cc746d69a46fe --- /dev/null +++ b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/Libbox.objc.h @@ -0,0 +1,811 @@ +// Objective-C API for talking to github.com/sagernet/sing-box/experimental/libbox Go package. +// gobind -lang=objc github.com/sagernet/sing-box/experimental/libbox +// +// File is generated by gobind. Do not edit. + +#ifndef __Libbox_H__ +#define __Libbox_H__ + +@import Foundation; +#include "ref.h" +#include "Universe.objc.h" + + +@class LibboxBoxService; +@class LibboxCommandClient; +@class LibboxCommandClientOptions; +@class LibboxCommandServer; +@class LibboxErrorMessage; +@class LibboxExchangeContext; +@class LibboxImportRemoteProfile; +@class LibboxNetworkInterface; +@class LibboxOutboundGroup; +@class LibboxOutboundGroupItem; +@class LibboxPProfServer; +@class LibboxProfileContent; +@class LibboxProfileContentRequest; +@class LibboxProfileDecoder; +@class LibboxProfileEncoder; +@class LibboxProfilePreview; +@class LibboxRoutePrefix; +@class LibboxStatusMessage; +@class LibboxSystemProxyStatus; +@class LibboxWIFIState; +@protocol LibboxCommandClientHandler; +@class LibboxCommandClientHandler; +@protocol LibboxCommandServerHandler; +@class LibboxCommandServerHandler; +@protocol LibboxFunc; +@class LibboxFunc; +@protocol LibboxHTTPClient; +@class LibboxHTTPClient; +@protocol LibboxHTTPRequest; +@class LibboxHTTPRequest; +@protocol LibboxHTTPResponse; +@class LibboxHTTPResponse; +@protocol LibboxInterfaceUpdateListener; +@class LibboxInterfaceUpdateListener; +@protocol LibboxLocalDNSTransport; +@class LibboxLocalDNSTransport; +@protocol LibboxNetworkInterfaceIterator; +@class LibboxNetworkInterfaceIterator; +@protocol LibboxOnDemandRule; +@class LibboxOnDemandRule; +@protocol LibboxOnDemandRuleIterator; +@class LibboxOnDemandRuleIterator; +@protocol LibboxOutboundGroupItemIterator; +@class LibboxOutboundGroupItemIterator; +@protocol LibboxOutboundGroupIterator; +@class LibboxOutboundGroupIterator; +@protocol LibboxPlatformInterface; +@class LibboxPlatformInterface; +@protocol LibboxProfilePreviewIterator; +@class LibboxProfilePreviewIterator; +@protocol LibboxRoutePrefixIterator; +@class LibboxRoutePrefixIterator; +@protocol LibboxStringIterator; +@class LibboxStringIterator; +@protocol LibboxTunInterface; +@class LibboxTunInterface; +@protocol LibboxTunOptions; +@class LibboxTunOptions; + +@protocol LibboxCommandClientHandler <NSObject> +- (void)clearLog; +- (void)connected; +- (void)disconnected:(NSString* _Nullable)message; +- (void)initializeClashMode:(id<LibboxStringIterator> _Nullable)modeList currentMode:(NSString* _Nullable)currentMode; +- (void)updateClashMode:(NSString* _Nullable)newMode; +- (void)writeGroups:(id<LibboxOutboundGroupIterator> _Nullable)message; +- (void)writeLog:(NSString* _Nullable)message; +- (void)writeStatus:(LibboxStatusMessage* _Nullable)message; +@end + +@protocol LibboxCommandServerHandler <NSObject> +- (LibboxSystemProxyStatus* _Nullable)getSystemProxyStatus; +- (void)postServiceClose; +- (BOOL)serviceReload:(NSError* _Nullable* _Nullable)error; +- (BOOL)setSystemProxyEnabled:(BOOL)isEnabled error:(NSError* _Nullable* _Nullable)error; +@end + +@protocol LibboxFunc <NSObject> +- (BOOL)invoke:(NSError* _Nullable* _Nullable)error; +@end + +@protocol LibboxHTTPClient <NSObject> +- (void)close; +- (void)keepAlive; +- (void)modernTLS; +- (id<LibboxHTTPRequest> _Nullable)newRequest; +- (void)pinnedSHA256:(NSString* _Nullable)sumHex; +- (void)pinnedTLS12; +- (void)restrictedTLS; +- (void)trySocks5:(int32_t)port; +@end + +@protocol LibboxHTTPRequest <NSObject> +- (id<LibboxHTTPResponse> _Nullable)execute:(NSError* _Nullable* _Nullable)error; +- (void)randomUserAgent; +- (void)setContent:(NSData* _Nullable)content; +- (void)setContentString:(NSString* _Nullable)content; +- (void)setHeader:(NSString* _Nullable)key value:(NSString* _Nullable)value; +- (void)setMethod:(NSString* _Nullable)method; +- (BOOL)setURL:(NSString* _Nullable)link error:(NSError* _Nullable* _Nullable)error; +- (void)setUserAgent:(NSString* _Nullable)userAgent; +@end + +@protocol LibboxHTTPResponse <NSObject> +- (NSData* _Nullable)getContent:(NSError* _Nullable* _Nullable)error; +- (NSString* _Nonnull)getContentString:(NSError* _Nullable* _Nullable)error; +- (BOOL)writeTo:(NSString* _Nullable)path error:(NSError* _Nullable* _Nullable)error; +@end + +@protocol LibboxInterfaceUpdateListener <NSObject> +- (void)updateDefaultInterface:(NSString* _Nullable)interfaceName interfaceIndex:(int32_t)interfaceIndex; +@end + +@protocol LibboxLocalDNSTransport <NSObject> +- (BOOL)exchange:(LibboxExchangeContext* _Nullable)ctx message:(NSData* _Nullable)message error:(NSError* _Nullable* _Nullable)error; +- (BOOL)lookup:(LibboxExchangeContext* _Nullable)ctx network:(NSString* _Nullable)network domain:(NSString* _Nullable)domain error:(NSError* _Nullable* _Nullable)error; +- (BOOL)raw; +@end + +@protocol LibboxNetworkInterfaceIterator <NSObject> +- (BOOL)hasNext; +- (LibboxNetworkInterface* _Nullable)next; +@end + +@protocol LibboxOnDemandRule <NSObject> +- (id<LibboxStringIterator> _Nullable)dnsSearchDomainMatch; +- (id<LibboxStringIterator> _Nullable)dnsServerAddressMatch; +- (int32_t)interfaceTypeMatch; +- (NSString* _Nonnull)probeURL; +- (id<LibboxStringIterator> _Nullable)ssidMatch; +- (int32_t)target; +@end + +@protocol LibboxOnDemandRuleIterator <NSObject> +- (BOOL)hasNext; +- (id<LibboxOnDemandRule> _Nullable)next; +@end + +@protocol LibboxOutboundGroupItemIterator <NSObject> +- (BOOL)hasNext; +- (LibboxOutboundGroupItem* _Nullable)next; +@end + +@protocol LibboxOutboundGroupIterator <NSObject> +- (BOOL)hasNext; +- (LibboxOutboundGroup* _Nullable)next; +@end + +@protocol LibboxPlatformInterface <NSObject> +- (BOOL)autoDetectInterfaceControl:(int32_t)fd error:(NSError* _Nullable* _Nullable)error; +- (void)clearDNSCache; +- (BOOL)closeDefaultInterfaceMonitor:(id<LibboxInterfaceUpdateListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; +- (BOOL)findConnectionOwner:(int32_t)ipProtocol sourceAddress:(NSString* _Nullable)sourceAddress sourcePort:(int32_t)sourcePort destinationAddress:(NSString* _Nullable)destinationAddress destinationPort:(int32_t)destinationPort ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (id<LibboxNetworkInterfaceIterator> _Nullable)getInterfaces:(NSError* _Nullable* _Nullable)error; +- (BOOL)includeAllNetworks; +- (BOOL)openTun:(id<LibboxTunOptions> _Nullable)options ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (NSString* _Nonnull)packageNameByUid:(int32_t)uid error:(NSError* _Nullable* _Nullable)error; +- (LibboxWIFIState* _Nullable)readWIFIState; +- (BOOL)startDefaultInterfaceMonitor:(id<LibboxInterfaceUpdateListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; +- (BOOL)uidByPackageName:(NSString* _Nullable)packageName ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (BOOL)underNetworkExtension; +- (BOOL)usePlatformAutoDetectInterfaceControl; +- (BOOL)usePlatformDefaultInterfaceMonitor; +- (BOOL)usePlatformInterfaceGetter; +- (BOOL)useProcFS; +- (void)writeLog:(NSString* _Nullable)message; +@end + +@protocol LibboxProfilePreviewIterator <NSObject> +- (BOOL)hasNext; +- (LibboxProfilePreview* _Nullable)next; +@end + +@protocol LibboxRoutePrefixIterator <NSObject> +- (BOOL)hasNext; +- (LibboxRoutePrefix* _Nullable)next; +@end + +@protocol LibboxStringIterator <NSObject> +- (BOOL)hasNext; +- (NSString* _Nonnull)next; +@end + +@protocol LibboxTunInterface <NSObject> +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (int32_t)fileDescriptor; +@end + +@protocol LibboxTunOptions <NSObject> +- (BOOL)getAutoRoute; +- (NSString* _Nonnull)getDNSServerAddress:(NSError* _Nullable* _Nullable)error; +- (id<LibboxStringIterator> _Nullable)getExcludePackage; +- (id<LibboxStringIterator> _Nullable)getHTTPProxyBypassDomain; +- (id<LibboxStringIterator> _Nullable)getHTTPProxyMatchDomain; +- (NSString* _Nonnull)getHTTPProxyServer; +- (int32_t)getHTTPProxyServerPort; +- (id<LibboxStringIterator> _Nullable)getIncludePackage; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4Address; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteExcludeAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteRange; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6Address; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteExcludeAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteRange; +- (int32_t)getMTU; +- (BOOL)getStrictRoute; +- (BOOL)isHTTPProxyEnabled; +@end + +@interface LibboxBoxService : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (BOOL)needWIFIState; +- (void)pause; +- (BOOL)start:(NSError* _Nullable* _Nullable)error; +- (void)wake; +@end + +@interface LibboxCommandClient : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nullable instancetype)init:(id<LibboxCommandClientHandler> _Nullable)handler options:(LibboxCommandClientOptions* _Nullable)options; +- (BOOL)closeConnections:(NSError* _Nullable* _Nullable)error; +- (BOOL)connect:(NSError* _Nullable* _Nullable)error; +- (BOOL)disconnect:(NSError* _Nullable* _Nullable)error; +- (LibboxSystemProxyStatus* _Nullable)getSystemProxyStatus:(NSError* _Nullable* _Nullable)error; +- (BOOL)selectOutbound:(NSString* _Nullable)groupTag outboundTag:(NSString* _Nullable)outboundTag error:(NSError* _Nullable* _Nullable)error; +- (BOOL)serviceClose:(NSError* _Nullable* _Nullable)error; +- (BOOL)serviceReload:(NSError* _Nullable* _Nullable)error; +- (BOOL)setClashMode:(NSString* _Nullable)newMode error:(NSError* _Nullable* _Nullable)error; +- (BOOL)setGroupExpand:(NSString* _Nullable)groupTag isExpand:(BOOL)isExpand error:(NSError* _Nullable* _Nullable)error; +- (BOOL)setSystemProxyEnabled:(BOOL)isEnabled error:(NSError* _Nullable* _Nullable)error; +- (BOOL)urlTest:(NSString* _Nullable)groupTag error:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxCommandClientOptions : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int32_t command; +@property (nonatomic) int64_t statusInterval; +@end + +@interface LibboxCommandServer : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nullable instancetype)init:(id<LibboxCommandServerHandler> _Nullable)handler maxLines:(int32_t)maxLines; +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (void)resetLog; +- (void)setService:(LibboxBoxService* _Nullable)newService; +- (BOOL)start:(NSError* _Nullable* _Nullable)error; +- (void)writeMessage:(NSString* _Nullable)message; +@end + +@interface LibboxErrorMessage : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull message; +- (NSData* _Nullable)encode; +@end + +@interface LibboxExchangeContext : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (void)errnoCode:(int32_t)code; +- (void)errorCode:(int32_t)code; +- (void)onCancel:(id<LibboxFunc> _Nullable)callback; +- (void)rawSuccess:(NSData* _Nullable)result; +- (void)success:(NSString* _Nullable)result; +@end + +@interface LibboxImportRemoteProfile : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull name; +@property (nonatomic) NSString* _Nonnull url; +@property (nonatomic) NSString* _Nonnull host; +@end + +@interface LibboxNetworkInterface : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int32_t index; +@property (nonatomic) int32_t mtu; +@property (nonatomic) NSString* _Nonnull name; +@property (nonatomic) id<LibboxStringIterator> _Nullable addresses; +@end + +@interface LibboxOutboundGroup : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull tag; +@property (nonatomic) NSString* _Nonnull type; +@property (nonatomic) BOOL selectable; +@property (nonatomic) NSString* _Nonnull selected; +@property (nonatomic) BOOL isExpand; +- (id<LibboxOutboundGroupItemIterator> _Nullable)getItems; +@end + +@interface LibboxOutboundGroupItem : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull tag; +@property (nonatomic) NSString* _Nonnull type; +@property (nonatomic) int64_t urlTestTime; +@property (nonatomic) int32_t urlTestDelay; +@end + +@interface LibboxPProfServer : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nullable instancetype)init:(long)port; +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (BOOL)start:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxProfileContent : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull name; +@property (nonatomic) int32_t type; +@property (nonatomic) NSString* _Nonnull config; +@property (nonatomic) NSString* _Nonnull remotePath; +@property (nonatomic) BOOL autoUpdate; +@property (nonatomic) int32_t autoUpdateInterval; +@property (nonatomic) int64_t lastUpdated; +- (NSData* _Nullable)encode; +@end + +@interface LibboxProfileContentRequest : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int64_t profileID; +- (NSData* _Nullable)encode; +@end + +@interface LibboxProfileDecoder : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (BOOL)decode:(NSData* _Nullable)data error:(NSError* _Nullable* _Nullable)error; +- (id<LibboxProfilePreviewIterator> _Nullable)iterator; +@end + +@interface LibboxProfileEncoder : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (void)append:(LibboxProfilePreview* _Nullable)profile; +- (NSData* _Nullable)encode; +@end + +@interface LibboxProfilePreview : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int64_t profileID; +@property (nonatomic) NSString* _Nonnull name; +@property (nonatomic) int32_t type; +@end + +@interface LibboxRoutePrefix : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (NSString* _Nonnull)address; +- (NSString* _Nonnull)mask; +- (int32_t)prefix; +- (NSString* _Nonnull)string; +@end + +@interface LibboxStatusMessage : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int64_t memory; +@property (nonatomic) int32_t goroutines; +@property (nonatomic) int32_t connectionsIn; +@property (nonatomic) int32_t connectionsOut; +@property (nonatomic) BOOL trafficAvailable; +@property (nonatomic) int64_t uplink; +@property (nonatomic) int64_t downlink; +@property (nonatomic) int64_t uplinkTotal; +@property (nonatomic) int64_t downlinkTotal; +@end + +@interface LibboxSystemProxyStatus : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) BOOL available; +@property (nonatomic) BOOL enabled; +@end + +@interface LibboxWIFIState : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nullable instancetype)init:(NSString* _Nullable)wifiSSID wifiBSSID:(NSString* _Nullable)wifiBSSID; +@property (nonatomic) NSString* _Nonnull ssid; +@property (nonatomic) NSString* _Nonnull bssid; +@end + +FOUNDATION_EXPORT const int32_t LibboxCommandClashMode; +FOUNDATION_EXPORT const int32_t LibboxCommandCloseConnections; +FOUNDATION_EXPORT const int32_t LibboxCommandGetSystemProxyStatus; +FOUNDATION_EXPORT const int32_t LibboxCommandGroup; +FOUNDATION_EXPORT const int32_t LibboxCommandGroupExpand; +FOUNDATION_EXPORT const int32_t LibboxCommandLog; +FOUNDATION_EXPORT const int32_t LibboxCommandSelectOutbound; +FOUNDATION_EXPORT const int32_t LibboxCommandServiceClose; +FOUNDATION_EXPORT const int32_t LibboxCommandServiceReload; +FOUNDATION_EXPORT const int32_t LibboxCommandSetClashMode; +FOUNDATION_EXPORT const int32_t LibboxCommandSetSystemProxyEnabled; +FOUNDATION_EXPORT const int32_t LibboxCommandStatus; +FOUNDATION_EXPORT const int32_t LibboxCommandURLTest; +FOUNDATION_EXPORT const int64_t LibboxMessageTypeError; +FOUNDATION_EXPORT const int64_t LibboxMessageTypeProfileContent; +FOUNDATION_EXPORT const int64_t LibboxMessageTypeProfileContentRequest; +FOUNDATION_EXPORT const int64_t LibboxMessageTypeProfileList; +FOUNDATION_EXPORT const int32_t LibboxProfileTypeLocal; +FOUNDATION_EXPORT const int32_t LibboxProfileTypeRemote; +FOUNDATION_EXPORT const int32_t LibboxProfileTypeiCloud; + +FOUNDATION_EXPORT BOOL LibboxCheckConfig(NSString* _Nullable configContent, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT void LibboxClearServiceError(void); + +FOUNDATION_EXPORT LibboxErrorMessage* _Nullable LibboxDecodeErrorMessage(NSData* _Nullable data, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT int32_t LibboxDecodeLengthChunk(NSData* _Nullable data); + +FOUNDATION_EXPORT LibboxProfileContent* _Nullable LibboxDecodeProfileContent(NSData* _Nullable data, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT LibboxProfileContentRequest* _Nullable LibboxDecodeProfileContentRequest(NSData* _Nullable data, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT NSData* _Nullable LibboxEncodeChunkedMessage(NSData* _Nullable data); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxFormatBytes(int64_t length); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxFormatConfig(NSString* _Nullable configContent, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxFormatMemoryBytes(int64_t length); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxGenerateRemoteProfileImportLink(NSString* _Nullable name, NSString* _Nullable remoteURL); + +FOUNDATION_EXPORT int32_t LibboxGetTunnelFileDescriptor(void); + +FOUNDATION_EXPORT LibboxCommandClient* _Nullable LibboxNewCommandClient(id<LibboxCommandClientHandler> _Nullable handler, LibboxCommandClientOptions* _Nullable options); + +FOUNDATION_EXPORT LibboxCommandServer* _Nullable LibboxNewCommandServer(id<LibboxCommandServerHandler> _Nullable handler, int32_t maxLines); + +FOUNDATION_EXPORT id<LibboxHTTPClient> _Nullable LibboxNewHTTPClient(void); + +FOUNDATION_EXPORT LibboxPProfServer* _Nullable LibboxNewPProfServer(long port); + +FOUNDATION_EXPORT LibboxBoxService* _Nullable LibboxNewService(NSString* _Nullable configContent, id<LibboxPlatformInterface> _Nullable platformInterface, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT LibboxCommandClient* _Nullable LibboxNewStandaloneCommandClient(void); + +FOUNDATION_EXPORT LibboxWIFIState* _Nullable LibboxNewWIFIState(NSString* _Nullable wifiSSID, NSString* _Nullable wifiBSSID); + +FOUNDATION_EXPORT LibboxImportRemoteProfile* _Nullable LibboxParseRemoteProfileImportLink(NSString* _Nullable importLink, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxProxyDisplayType(NSString* _Nullable proxyType); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxReadServiceError(NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT BOOL LibboxRedirectStderr(NSString* _Nullable path, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT void LibboxRegisterLocalDNSTransport(id<LibboxLocalDNSTransport> _Nullable transport); + +FOUNDATION_EXPORT void LibboxSetMemoryLimit(BOOL enabled); + +FOUNDATION_EXPORT void LibboxSetup(NSString* _Nullable basePath, NSString* _Nullable workingPath, NSString* _Nullable tempPath, BOOL isTVOS); + +FOUNDATION_EXPORT BOOL LibboxSetupWithUsername(NSString* _Nullable basePath, NSString* _Nullable workingPath, NSString* _Nullable tempPath, NSString* _Nullable username, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxVersion(void); + +FOUNDATION_EXPORT BOOL LibboxWriteServiceError(NSString* _Nullable message, NSError* _Nullable* _Nullable error); + +@class LibboxCommandClientHandler; + +@class LibboxCommandServerHandler; + +@class LibboxFunc; + +@class LibboxHTTPClient; + +@class LibboxHTTPRequest; + +@class LibboxHTTPResponse; + +@class LibboxInterfaceUpdateListener; + +@class LibboxLocalDNSTransport; + +@class LibboxNetworkInterfaceIterator; + +@class LibboxOnDemandRule; + +@class LibboxOnDemandRuleIterator; + +@class LibboxOutboundGroupItemIterator; + +@class LibboxOutboundGroupIterator; + +@class LibboxPlatformInterface; + +@class LibboxProfilePreviewIterator; + +@class LibboxRoutePrefixIterator; + +@class LibboxStringIterator; + +@class LibboxTunInterface; + +@class LibboxTunOptions; + +@interface LibboxCommandClientHandler : NSObject <goSeqRefInterface, LibboxCommandClientHandler> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (void)clearLog; +- (void)connected; +- (void)disconnected:(NSString* _Nullable)message; +- (void)initializeClashMode:(id<LibboxStringIterator> _Nullable)modeList currentMode:(NSString* _Nullable)currentMode; +- (void)updateClashMode:(NSString* _Nullable)newMode; +- (void)writeGroups:(id<LibboxOutboundGroupIterator> _Nullable)message; +- (void)writeLog:(NSString* _Nullable)message; +- (void)writeStatus:(LibboxStatusMessage* _Nullable)message; +@end + +@interface LibboxCommandServerHandler : NSObject <goSeqRefInterface, LibboxCommandServerHandler> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (LibboxSystemProxyStatus* _Nullable)getSystemProxyStatus; +- (void)postServiceClose; +- (BOOL)serviceReload:(NSError* _Nullable* _Nullable)error; +- (BOOL)setSystemProxyEnabled:(BOOL)isEnabled error:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxFunc : NSObject <goSeqRefInterface, LibboxFunc> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)invoke:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxHTTPClient : NSObject <goSeqRefInterface, LibboxHTTPClient> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (void)close; +- (void)keepAlive; +- (void)modernTLS; +- (id<LibboxHTTPRequest> _Nullable)newRequest; +- (void)pinnedSHA256:(NSString* _Nullable)sumHex; +- (void)pinnedTLS12; +- (void)restrictedTLS; +- (void)trySocks5:(int32_t)port; +@end + +@interface LibboxHTTPRequest : NSObject <goSeqRefInterface, LibboxHTTPRequest> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (id<LibboxHTTPResponse> _Nullable)execute:(NSError* _Nullable* _Nullable)error; +- (void)randomUserAgent; +- (void)setContent:(NSData* _Nullable)content; +- (void)setContentString:(NSString* _Nullable)content; +- (void)setHeader:(NSString* _Nullable)key value:(NSString* _Nullable)value; +- (void)setMethod:(NSString* _Nullable)method; +- (BOOL)setURL:(NSString* _Nullable)link error:(NSError* _Nullable* _Nullable)error; +- (void)setUserAgent:(NSString* _Nullable)userAgent; +@end + +@interface LibboxHTTPResponse : NSObject <goSeqRefInterface, LibboxHTTPResponse> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (NSData* _Nullable)getContent:(NSError* _Nullable* _Nullable)error; +- (NSString* _Nonnull)getContentString:(NSError* _Nullable* _Nullable)error; +- (BOOL)writeTo:(NSString* _Nullable)path error:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxInterfaceUpdateListener : NSObject <goSeqRefInterface, LibboxInterfaceUpdateListener> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (void)updateDefaultInterface:(NSString* _Nullable)interfaceName interfaceIndex:(int32_t)interfaceIndex; +@end + +@interface LibboxLocalDNSTransport : NSObject <goSeqRefInterface, LibboxLocalDNSTransport> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)exchange:(LibboxExchangeContext* _Nullable)ctx message:(NSData* _Nullable)message error:(NSError* _Nullable* _Nullable)error; +- (BOOL)lookup:(LibboxExchangeContext* _Nullable)ctx network:(NSString* _Nullable)network domain:(NSString* _Nullable)domain error:(NSError* _Nullable* _Nullable)error; +- (BOOL)raw; +@end + +@interface LibboxNetworkInterfaceIterator : NSObject <goSeqRefInterface, LibboxNetworkInterfaceIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxNetworkInterface* _Nullable)next; +@end + +@interface LibboxOnDemandRule : NSObject <goSeqRefInterface, LibboxOnDemandRule> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (id<LibboxStringIterator> _Nullable)dnsSearchDomainMatch; +- (id<LibboxStringIterator> _Nullable)dnsServerAddressMatch; +- (int32_t)interfaceTypeMatch; +- (NSString* _Nonnull)probeURL; +- (id<LibboxStringIterator> _Nullable)ssidMatch; +- (int32_t)target; +@end + +@interface LibboxOnDemandRuleIterator : NSObject <goSeqRefInterface, LibboxOnDemandRuleIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (id<LibboxOnDemandRule> _Nullable)next; +@end + +@interface LibboxOutboundGroupItemIterator : NSObject <goSeqRefInterface, LibboxOutboundGroupItemIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxOutboundGroupItem* _Nullable)next; +@end + +@interface LibboxOutboundGroupIterator : NSObject <goSeqRefInterface, LibboxOutboundGroupIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxOutboundGroup* _Nullable)next; +@end + +@interface LibboxPlatformInterface : NSObject <goSeqRefInterface, LibboxPlatformInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)autoDetectInterfaceControl:(int32_t)fd error:(NSError* _Nullable* _Nullable)error; +- (void)clearDNSCache; +- (BOOL)closeDefaultInterfaceMonitor:(id<LibboxInterfaceUpdateListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; +- (BOOL)findConnectionOwner:(int32_t)ipProtocol sourceAddress:(NSString* _Nullable)sourceAddress sourcePort:(int32_t)sourcePort destinationAddress:(NSString* _Nullable)destinationAddress destinationPort:(int32_t)destinationPort ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (id<LibboxNetworkInterfaceIterator> _Nullable)getInterfaces:(NSError* _Nullable* _Nullable)error; +- (BOOL)includeAllNetworks; +- (BOOL)openTun:(id<LibboxTunOptions> _Nullable)options ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (NSString* _Nonnull)packageNameByUid:(int32_t)uid error:(NSError* _Nullable* _Nullable)error; +- (LibboxWIFIState* _Nullable)readWIFIState; +- (BOOL)startDefaultInterfaceMonitor:(id<LibboxInterfaceUpdateListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; +- (BOOL)uidByPackageName:(NSString* _Nullable)packageName ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (BOOL)underNetworkExtension; +- (BOOL)usePlatformAutoDetectInterfaceControl; +- (BOOL)usePlatformDefaultInterfaceMonitor; +- (BOOL)usePlatformInterfaceGetter; +- (BOOL)useProcFS; +- (void)writeLog:(NSString* _Nullable)message; +@end + +@interface LibboxProfilePreviewIterator : NSObject <goSeqRefInterface, LibboxProfilePreviewIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxProfilePreview* _Nullable)next; +@end + +@interface LibboxRoutePrefixIterator : NSObject <goSeqRefInterface, LibboxRoutePrefixIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxRoutePrefix* _Nullable)next; +@end + +@interface LibboxStringIterator : NSObject <goSeqRefInterface, LibboxStringIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (NSString* _Nonnull)next; +@end + +@interface LibboxTunInterface : NSObject <goSeqRefInterface, LibboxTunInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (int32_t)fileDescriptor; +@end + +@interface LibboxTunOptions : NSObject <goSeqRefInterface, LibboxTunOptions> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)getAutoRoute; +- (NSString* _Nonnull)getDNSServerAddress:(NSError* _Nullable* _Nullable)error; +- (id<LibboxStringIterator> _Nullable)getExcludePackage; +- (id<LibboxStringIterator> _Nullable)getHTTPProxyBypassDomain; +- (id<LibboxStringIterator> _Nullable)getHTTPProxyMatchDomain; +- (NSString* _Nonnull)getHTTPProxyServer; +- (int32_t)getHTTPProxyServerPort; +- (id<LibboxStringIterator> _Nullable)getIncludePackage; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4Address; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteExcludeAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteRange; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6Address; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteExcludeAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteRange; +- (int32_t)getMTU; +- (BOOL)getStrictRoute; +- (BOOL)isHTTPProxyEnabled; +@end + +#endif diff --git a/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/Universe.objc.h b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/Universe.objc.h new file mode 100644 index 0000000000000000000000000000000000000000..019e7502d581983722a15bf30799e85cbc5dd766 --- /dev/null +++ b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/Universe.objc.h @@ -0,0 +1,29 @@ +// Objective-C API for talking to Go package. +// gobind -lang=objc +// +// File is generated by gobind. Do not edit. + +#ifndef __Universe_H__ +#define __Universe_H__ + +@import Foundation; +#include "ref.h" + +@protocol Universeerror; +@class Universeerror; + +@protocol Universeerror <NSObject> +- (NSString* _Nonnull)error; +@end + +@class Universeerror; + +@interface Universeerror : NSError <goSeqRefInterface, Universeerror> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (NSString* _Nonnull)error; +@end + +#endif diff --git a/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/ref.h b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/ref.h new file mode 100644 index 0000000000000000000000000000000000000000..b8036a4d85c7387f3def61473a071b5d8c4c8208 --- /dev/null +++ b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/ref.h @@ -0,0 +1,35 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef __GO_REF_HDR__ +#define __GO_REF_HDR__ + +#include <Foundation/Foundation.h> + +// GoSeqRef is an object tagged with an integer for passing back and +// forth across the language boundary. A GoSeqRef may represent either +// an instance of a Go object, or an Objective-C object passed to Go. +// The explicit allocation of a GoSeqRef is used to pin a Go object +// when it is passed to Objective-C. The Go seq package maintains a +// reference to the Go object in a map keyed by the refnum along with +// a reference count. When the reference count reaches zero, the Go +// seq package will clear the corresponding entry in the map. +@interface GoSeqRef : NSObject { +} +@property(readonly) int32_t refnum; +@property(strong) id obj; // NULL when representing a Go object. + +// new GoSeqRef object to proxy a Go object. The refnum must be +// provided from Go side. +- (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj; + +- (int32_t)incNum; + +@end + +@protocol goSeqRefInterface +-(GoSeqRef*) _ref; +@end + +#endif diff --git a/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Libbox b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Libbox new file mode 100644 index 0000000000000000000000000000000000000000..e81ff5d067c40a7bd3b4f64542908d96b71f62e1 Binary files /dev/null and b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Libbox differ diff --git a/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Modules/module.modulemap b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Modules/module.modulemap new file mode 100644 index 0000000000000000000000000000000000000000..1ff5e29bdfd41d06429a16857384ad8cbffb85fe --- /dev/null +++ b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Modules/module.modulemap @@ -0,0 +1,8 @@ +framework module "Libbox" { + header "ref.h" + header "Libbox.objc.h" + header "Universe.objc.h" + header "Libbox.h" + + export * +} \ No newline at end of file diff --git a/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Resources/Info.plist b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Resources/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..0d1a4b8ab9b1fc8e9357197398f73353470cb636 --- /dev/null +++ b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Resources/Info.plist @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> + <plist version="1.0"> + <dict> + </dict> + </plist> diff --git a/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/Current b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/Current new file mode 120000 index 0000000000000000000000000000000000000000..8c7e5a667f1b771847fe88c01c3de34413a1b220 --- /dev/null +++ b/Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/Current @@ -0,0 +1 @@ +A \ No newline at end of file diff --git a/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Headers b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Headers new file mode 120000 index 0000000000000000000000000000000000000000..a177d2a6b92600696030834c319f5e1434f9ee6a --- /dev/null +++ b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Libbox b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Libbox new file mode 120000 index 0000000000000000000000000000000000000000..97ad87691145a5467708b9e044f9e0e4be79a763 --- /dev/null +++ b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Libbox @@ -0,0 +1 @@ +Versions/Current/Libbox \ No newline at end of file diff --git a/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Modules b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Modules new file mode 120000 index 0000000000000000000000000000000000000000..5736f3186e797b8b787748c9979d0fed3b0536c3 --- /dev/null +++ b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Modules @@ -0,0 +1 @@ +Versions/Current/Modules \ No newline at end of file diff --git a/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Resources b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Resources new file mode 120000 index 0000000000000000000000000000000000000000..953ee36f3bb709faf58a351e0b33c353e337c0a2 --- /dev/null +++ b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Resources @@ -0,0 +1 @@ +Versions/Current/Resources \ No newline at end of file diff --git a/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Headers/Libbox.h b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Headers/Libbox.h new file mode 100644 index 0000000000000000000000000000000000000000..7e3aff43ac0a1cd946c152e9547907ff842adc70 --- /dev/null +++ b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Headers/Libbox.h @@ -0,0 +1,13 @@ + +// Objective-C API for talking to the following Go packages +// +// github.com/sagernet/sing-box/experimental/libbox +// +// File is generated by gomobile bind. Do not edit. +#ifndef __Libbox_FRAMEWORK_H__ +#define __Libbox_FRAMEWORK_H__ + +#include "Libbox.objc.h" +#include "Universe.objc.h" + +#endif diff --git a/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Headers/Libbox.objc.h b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Headers/Libbox.objc.h new file mode 100644 index 0000000000000000000000000000000000000000..98f33b9c58db878543752eb7027cc746d69a46fe --- /dev/null +++ b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Headers/Libbox.objc.h @@ -0,0 +1,811 @@ +// Objective-C API for talking to github.com/sagernet/sing-box/experimental/libbox Go package. +// gobind -lang=objc github.com/sagernet/sing-box/experimental/libbox +// +// File is generated by gobind. Do not edit. + +#ifndef __Libbox_H__ +#define __Libbox_H__ + +@import Foundation; +#include "ref.h" +#include "Universe.objc.h" + + +@class LibboxBoxService; +@class LibboxCommandClient; +@class LibboxCommandClientOptions; +@class LibboxCommandServer; +@class LibboxErrorMessage; +@class LibboxExchangeContext; +@class LibboxImportRemoteProfile; +@class LibboxNetworkInterface; +@class LibboxOutboundGroup; +@class LibboxOutboundGroupItem; +@class LibboxPProfServer; +@class LibboxProfileContent; +@class LibboxProfileContentRequest; +@class LibboxProfileDecoder; +@class LibboxProfileEncoder; +@class LibboxProfilePreview; +@class LibboxRoutePrefix; +@class LibboxStatusMessage; +@class LibboxSystemProxyStatus; +@class LibboxWIFIState; +@protocol LibboxCommandClientHandler; +@class LibboxCommandClientHandler; +@protocol LibboxCommandServerHandler; +@class LibboxCommandServerHandler; +@protocol LibboxFunc; +@class LibboxFunc; +@protocol LibboxHTTPClient; +@class LibboxHTTPClient; +@protocol LibboxHTTPRequest; +@class LibboxHTTPRequest; +@protocol LibboxHTTPResponse; +@class LibboxHTTPResponse; +@protocol LibboxInterfaceUpdateListener; +@class LibboxInterfaceUpdateListener; +@protocol LibboxLocalDNSTransport; +@class LibboxLocalDNSTransport; +@protocol LibboxNetworkInterfaceIterator; +@class LibboxNetworkInterfaceIterator; +@protocol LibboxOnDemandRule; +@class LibboxOnDemandRule; +@protocol LibboxOnDemandRuleIterator; +@class LibboxOnDemandRuleIterator; +@protocol LibboxOutboundGroupItemIterator; +@class LibboxOutboundGroupItemIterator; +@protocol LibboxOutboundGroupIterator; +@class LibboxOutboundGroupIterator; +@protocol LibboxPlatformInterface; +@class LibboxPlatformInterface; +@protocol LibboxProfilePreviewIterator; +@class LibboxProfilePreviewIterator; +@protocol LibboxRoutePrefixIterator; +@class LibboxRoutePrefixIterator; +@protocol LibboxStringIterator; +@class LibboxStringIterator; +@protocol LibboxTunInterface; +@class LibboxTunInterface; +@protocol LibboxTunOptions; +@class LibboxTunOptions; + +@protocol LibboxCommandClientHandler <NSObject> +- (void)clearLog; +- (void)connected; +- (void)disconnected:(NSString* _Nullable)message; +- (void)initializeClashMode:(id<LibboxStringIterator> _Nullable)modeList currentMode:(NSString* _Nullable)currentMode; +- (void)updateClashMode:(NSString* _Nullable)newMode; +- (void)writeGroups:(id<LibboxOutboundGroupIterator> _Nullable)message; +- (void)writeLog:(NSString* _Nullable)message; +- (void)writeStatus:(LibboxStatusMessage* _Nullable)message; +@end + +@protocol LibboxCommandServerHandler <NSObject> +- (LibboxSystemProxyStatus* _Nullable)getSystemProxyStatus; +- (void)postServiceClose; +- (BOOL)serviceReload:(NSError* _Nullable* _Nullable)error; +- (BOOL)setSystemProxyEnabled:(BOOL)isEnabled error:(NSError* _Nullable* _Nullable)error; +@end + +@protocol LibboxFunc <NSObject> +- (BOOL)invoke:(NSError* _Nullable* _Nullable)error; +@end + +@protocol LibboxHTTPClient <NSObject> +- (void)close; +- (void)keepAlive; +- (void)modernTLS; +- (id<LibboxHTTPRequest> _Nullable)newRequest; +- (void)pinnedSHA256:(NSString* _Nullable)sumHex; +- (void)pinnedTLS12; +- (void)restrictedTLS; +- (void)trySocks5:(int32_t)port; +@end + +@protocol LibboxHTTPRequest <NSObject> +- (id<LibboxHTTPResponse> _Nullable)execute:(NSError* _Nullable* _Nullable)error; +- (void)randomUserAgent; +- (void)setContent:(NSData* _Nullable)content; +- (void)setContentString:(NSString* _Nullable)content; +- (void)setHeader:(NSString* _Nullable)key value:(NSString* _Nullable)value; +- (void)setMethod:(NSString* _Nullable)method; +- (BOOL)setURL:(NSString* _Nullable)link error:(NSError* _Nullable* _Nullable)error; +- (void)setUserAgent:(NSString* _Nullable)userAgent; +@end + +@protocol LibboxHTTPResponse <NSObject> +- (NSData* _Nullable)getContent:(NSError* _Nullable* _Nullable)error; +- (NSString* _Nonnull)getContentString:(NSError* _Nullable* _Nullable)error; +- (BOOL)writeTo:(NSString* _Nullable)path error:(NSError* _Nullable* _Nullable)error; +@end + +@protocol LibboxInterfaceUpdateListener <NSObject> +- (void)updateDefaultInterface:(NSString* _Nullable)interfaceName interfaceIndex:(int32_t)interfaceIndex; +@end + +@protocol LibboxLocalDNSTransport <NSObject> +- (BOOL)exchange:(LibboxExchangeContext* _Nullable)ctx message:(NSData* _Nullable)message error:(NSError* _Nullable* _Nullable)error; +- (BOOL)lookup:(LibboxExchangeContext* _Nullable)ctx network:(NSString* _Nullable)network domain:(NSString* _Nullable)domain error:(NSError* _Nullable* _Nullable)error; +- (BOOL)raw; +@end + +@protocol LibboxNetworkInterfaceIterator <NSObject> +- (BOOL)hasNext; +- (LibboxNetworkInterface* _Nullable)next; +@end + +@protocol LibboxOnDemandRule <NSObject> +- (id<LibboxStringIterator> _Nullable)dnsSearchDomainMatch; +- (id<LibboxStringIterator> _Nullable)dnsServerAddressMatch; +- (int32_t)interfaceTypeMatch; +- (NSString* _Nonnull)probeURL; +- (id<LibboxStringIterator> _Nullable)ssidMatch; +- (int32_t)target; +@end + +@protocol LibboxOnDemandRuleIterator <NSObject> +- (BOOL)hasNext; +- (id<LibboxOnDemandRule> _Nullable)next; +@end + +@protocol LibboxOutboundGroupItemIterator <NSObject> +- (BOOL)hasNext; +- (LibboxOutboundGroupItem* _Nullable)next; +@end + +@protocol LibboxOutboundGroupIterator <NSObject> +- (BOOL)hasNext; +- (LibboxOutboundGroup* _Nullable)next; +@end + +@protocol LibboxPlatformInterface <NSObject> +- (BOOL)autoDetectInterfaceControl:(int32_t)fd error:(NSError* _Nullable* _Nullable)error; +- (void)clearDNSCache; +- (BOOL)closeDefaultInterfaceMonitor:(id<LibboxInterfaceUpdateListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; +- (BOOL)findConnectionOwner:(int32_t)ipProtocol sourceAddress:(NSString* _Nullable)sourceAddress sourcePort:(int32_t)sourcePort destinationAddress:(NSString* _Nullable)destinationAddress destinationPort:(int32_t)destinationPort ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (id<LibboxNetworkInterfaceIterator> _Nullable)getInterfaces:(NSError* _Nullable* _Nullable)error; +- (BOOL)includeAllNetworks; +- (BOOL)openTun:(id<LibboxTunOptions> _Nullable)options ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (NSString* _Nonnull)packageNameByUid:(int32_t)uid error:(NSError* _Nullable* _Nullable)error; +- (LibboxWIFIState* _Nullable)readWIFIState; +- (BOOL)startDefaultInterfaceMonitor:(id<LibboxInterfaceUpdateListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; +- (BOOL)uidByPackageName:(NSString* _Nullable)packageName ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (BOOL)underNetworkExtension; +- (BOOL)usePlatformAutoDetectInterfaceControl; +- (BOOL)usePlatformDefaultInterfaceMonitor; +- (BOOL)usePlatformInterfaceGetter; +- (BOOL)useProcFS; +- (void)writeLog:(NSString* _Nullable)message; +@end + +@protocol LibboxProfilePreviewIterator <NSObject> +- (BOOL)hasNext; +- (LibboxProfilePreview* _Nullable)next; +@end + +@protocol LibboxRoutePrefixIterator <NSObject> +- (BOOL)hasNext; +- (LibboxRoutePrefix* _Nullable)next; +@end + +@protocol LibboxStringIterator <NSObject> +- (BOOL)hasNext; +- (NSString* _Nonnull)next; +@end + +@protocol LibboxTunInterface <NSObject> +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (int32_t)fileDescriptor; +@end + +@protocol LibboxTunOptions <NSObject> +- (BOOL)getAutoRoute; +- (NSString* _Nonnull)getDNSServerAddress:(NSError* _Nullable* _Nullable)error; +- (id<LibboxStringIterator> _Nullable)getExcludePackage; +- (id<LibboxStringIterator> _Nullable)getHTTPProxyBypassDomain; +- (id<LibboxStringIterator> _Nullable)getHTTPProxyMatchDomain; +- (NSString* _Nonnull)getHTTPProxyServer; +- (int32_t)getHTTPProxyServerPort; +- (id<LibboxStringIterator> _Nullable)getIncludePackage; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4Address; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteExcludeAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteRange; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6Address; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteExcludeAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteRange; +- (int32_t)getMTU; +- (BOOL)getStrictRoute; +- (BOOL)isHTTPProxyEnabled; +@end + +@interface LibboxBoxService : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (BOOL)needWIFIState; +- (void)pause; +- (BOOL)start:(NSError* _Nullable* _Nullable)error; +- (void)wake; +@end + +@interface LibboxCommandClient : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nullable instancetype)init:(id<LibboxCommandClientHandler> _Nullable)handler options:(LibboxCommandClientOptions* _Nullable)options; +- (BOOL)closeConnections:(NSError* _Nullable* _Nullable)error; +- (BOOL)connect:(NSError* _Nullable* _Nullable)error; +- (BOOL)disconnect:(NSError* _Nullable* _Nullable)error; +- (LibboxSystemProxyStatus* _Nullable)getSystemProxyStatus:(NSError* _Nullable* _Nullable)error; +- (BOOL)selectOutbound:(NSString* _Nullable)groupTag outboundTag:(NSString* _Nullable)outboundTag error:(NSError* _Nullable* _Nullable)error; +- (BOOL)serviceClose:(NSError* _Nullable* _Nullable)error; +- (BOOL)serviceReload:(NSError* _Nullable* _Nullable)error; +- (BOOL)setClashMode:(NSString* _Nullable)newMode error:(NSError* _Nullable* _Nullable)error; +- (BOOL)setGroupExpand:(NSString* _Nullable)groupTag isExpand:(BOOL)isExpand error:(NSError* _Nullable* _Nullable)error; +- (BOOL)setSystemProxyEnabled:(BOOL)isEnabled error:(NSError* _Nullable* _Nullable)error; +- (BOOL)urlTest:(NSString* _Nullable)groupTag error:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxCommandClientOptions : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int32_t command; +@property (nonatomic) int64_t statusInterval; +@end + +@interface LibboxCommandServer : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nullable instancetype)init:(id<LibboxCommandServerHandler> _Nullable)handler maxLines:(int32_t)maxLines; +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (void)resetLog; +- (void)setService:(LibboxBoxService* _Nullable)newService; +- (BOOL)start:(NSError* _Nullable* _Nullable)error; +- (void)writeMessage:(NSString* _Nullable)message; +@end + +@interface LibboxErrorMessage : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull message; +- (NSData* _Nullable)encode; +@end + +@interface LibboxExchangeContext : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (void)errnoCode:(int32_t)code; +- (void)errorCode:(int32_t)code; +- (void)onCancel:(id<LibboxFunc> _Nullable)callback; +- (void)rawSuccess:(NSData* _Nullable)result; +- (void)success:(NSString* _Nullable)result; +@end + +@interface LibboxImportRemoteProfile : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull name; +@property (nonatomic) NSString* _Nonnull url; +@property (nonatomic) NSString* _Nonnull host; +@end + +@interface LibboxNetworkInterface : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int32_t index; +@property (nonatomic) int32_t mtu; +@property (nonatomic) NSString* _Nonnull name; +@property (nonatomic) id<LibboxStringIterator> _Nullable addresses; +@end + +@interface LibboxOutboundGroup : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull tag; +@property (nonatomic) NSString* _Nonnull type; +@property (nonatomic) BOOL selectable; +@property (nonatomic) NSString* _Nonnull selected; +@property (nonatomic) BOOL isExpand; +- (id<LibboxOutboundGroupItemIterator> _Nullable)getItems; +@end + +@interface LibboxOutboundGroupItem : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull tag; +@property (nonatomic) NSString* _Nonnull type; +@property (nonatomic) int64_t urlTestTime; +@property (nonatomic) int32_t urlTestDelay; +@end + +@interface LibboxPProfServer : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nullable instancetype)init:(long)port; +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (BOOL)start:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxProfileContent : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull name; +@property (nonatomic) int32_t type; +@property (nonatomic) NSString* _Nonnull config; +@property (nonatomic) NSString* _Nonnull remotePath; +@property (nonatomic) BOOL autoUpdate; +@property (nonatomic) int32_t autoUpdateInterval; +@property (nonatomic) int64_t lastUpdated; +- (NSData* _Nullable)encode; +@end + +@interface LibboxProfileContentRequest : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int64_t profileID; +- (NSData* _Nullable)encode; +@end + +@interface LibboxProfileDecoder : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (BOOL)decode:(NSData* _Nullable)data error:(NSError* _Nullable* _Nullable)error; +- (id<LibboxProfilePreviewIterator> _Nullable)iterator; +@end + +@interface LibboxProfileEncoder : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (void)append:(LibboxProfilePreview* _Nullable)profile; +- (NSData* _Nullable)encode; +@end + +@interface LibboxProfilePreview : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int64_t profileID; +@property (nonatomic) NSString* _Nonnull name; +@property (nonatomic) int32_t type; +@end + +@interface LibboxRoutePrefix : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (NSString* _Nonnull)address; +- (NSString* _Nonnull)mask; +- (int32_t)prefix; +- (NSString* _Nonnull)string; +@end + +@interface LibboxStatusMessage : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int64_t memory; +@property (nonatomic) int32_t goroutines; +@property (nonatomic) int32_t connectionsIn; +@property (nonatomic) int32_t connectionsOut; +@property (nonatomic) BOOL trafficAvailable; +@property (nonatomic) int64_t uplink; +@property (nonatomic) int64_t downlink; +@property (nonatomic) int64_t uplinkTotal; +@property (nonatomic) int64_t downlinkTotal; +@end + +@interface LibboxSystemProxyStatus : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) BOOL available; +@property (nonatomic) BOOL enabled; +@end + +@interface LibboxWIFIState : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nullable instancetype)init:(NSString* _Nullable)wifiSSID wifiBSSID:(NSString* _Nullable)wifiBSSID; +@property (nonatomic) NSString* _Nonnull ssid; +@property (nonatomic) NSString* _Nonnull bssid; +@end + +FOUNDATION_EXPORT const int32_t LibboxCommandClashMode; +FOUNDATION_EXPORT const int32_t LibboxCommandCloseConnections; +FOUNDATION_EXPORT const int32_t LibboxCommandGetSystemProxyStatus; +FOUNDATION_EXPORT const int32_t LibboxCommandGroup; +FOUNDATION_EXPORT const int32_t LibboxCommandGroupExpand; +FOUNDATION_EXPORT const int32_t LibboxCommandLog; +FOUNDATION_EXPORT const int32_t LibboxCommandSelectOutbound; +FOUNDATION_EXPORT const int32_t LibboxCommandServiceClose; +FOUNDATION_EXPORT const int32_t LibboxCommandServiceReload; +FOUNDATION_EXPORT const int32_t LibboxCommandSetClashMode; +FOUNDATION_EXPORT const int32_t LibboxCommandSetSystemProxyEnabled; +FOUNDATION_EXPORT const int32_t LibboxCommandStatus; +FOUNDATION_EXPORT const int32_t LibboxCommandURLTest; +FOUNDATION_EXPORT const int64_t LibboxMessageTypeError; +FOUNDATION_EXPORT const int64_t LibboxMessageTypeProfileContent; +FOUNDATION_EXPORT const int64_t LibboxMessageTypeProfileContentRequest; +FOUNDATION_EXPORT const int64_t LibboxMessageTypeProfileList; +FOUNDATION_EXPORT const int32_t LibboxProfileTypeLocal; +FOUNDATION_EXPORT const int32_t LibboxProfileTypeRemote; +FOUNDATION_EXPORT const int32_t LibboxProfileTypeiCloud; + +FOUNDATION_EXPORT BOOL LibboxCheckConfig(NSString* _Nullable configContent, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT void LibboxClearServiceError(void); + +FOUNDATION_EXPORT LibboxErrorMessage* _Nullable LibboxDecodeErrorMessage(NSData* _Nullable data, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT int32_t LibboxDecodeLengthChunk(NSData* _Nullable data); + +FOUNDATION_EXPORT LibboxProfileContent* _Nullable LibboxDecodeProfileContent(NSData* _Nullable data, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT LibboxProfileContentRequest* _Nullable LibboxDecodeProfileContentRequest(NSData* _Nullable data, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT NSData* _Nullable LibboxEncodeChunkedMessage(NSData* _Nullable data); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxFormatBytes(int64_t length); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxFormatConfig(NSString* _Nullable configContent, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxFormatMemoryBytes(int64_t length); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxGenerateRemoteProfileImportLink(NSString* _Nullable name, NSString* _Nullable remoteURL); + +FOUNDATION_EXPORT int32_t LibboxGetTunnelFileDescriptor(void); + +FOUNDATION_EXPORT LibboxCommandClient* _Nullable LibboxNewCommandClient(id<LibboxCommandClientHandler> _Nullable handler, LibboxCommandClientOptions* _Nullable options); + +FOUNDATION_EXPORT LibboxCommandServer* _Nullable LibboxNewCommandServer(id<LibboxCommandServerHandler> _Nullable handler, int32_t maxLines); + +FOUNDATION_EXPORT id<LibboxHTTPClient> _Nullable LibboxNewHTTPClient(void); + +FOUNDATION_EXPORT LibboxPProfServer* _Nullable LibboxNewPProfServer(long port); + +FOUNDATION_EXPORT LibboxBoxService* _Nullable LibboxNewService(NSString* _Nullable configContent, id<LibboxPlatformInterface> _Nullable platformInterface, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT LibboxCommandClient* _Nullable LibboxNewStandaloneCommandClient(void); + +FOUNDATION_EXPORT LibboxWIFIState* _Nullable LibboxNewWIFIState(NSString* _Nullable wifiSSID, NSString* _Nullable wifiBSSID); + +FOUNDATION_EXPORT LibboxImportRemoteProfile* _Nullable LibboxParseRemoteProfileImportLink(NSString* _Nullable importLink, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxProxyDisplayType(NSString* _Nullable proxyType); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxReadServiceError(NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT BOOL LibboxRedirectStderr(NSString* _Nullable path, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT void LibboxRegisterLocalDNSTransport(id<LibboxLocalDNSTransport> _Nullable transport); + +FOUNDATION_EXPORT void LibboxSetMemoryLimit(BOOL enabled); + +FOUNDATION_EXPORT void LibboxSetup(NSString* _Nullable basePath, NSString* _Nullable workingPath, NSString* _Nullable tempPath, BOOL isTVOS); + +FOUNDATION_EXPORT BOOL LibboxSetupWithUsername(NSString* _Nullable basePath, NSString* _Nullable workingPath, NSString* _Nullable tempPath, NSString* _Nullable username, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxVersion(void); + +FOUNDATION_EXPORT BOOL LibboxWriteServiceError(NSString* _Nullable message, NSError* _Nullable* _Nullable error); + +@class LibboxCommandClientHandler; + +@class LibboxCommandServerHandler; + +@class LibboxFunc; + +@class LibboxHTTPClient; + +@class LibboxHTTPRequest; + +@class LibboxHTTPResponse; + +@class LibboxInterfaceUpdateListener; + +@class LibboxLocalDNSTransport; + +@class LibboxNetworkInterfaceIterator; + +@class LibboxOnDemandRule; + +@class LibboxOnDemandRuleIterator; + +@class LibboxOutboundGroupItemIterator; + +@class LibboxOutboundGroupIterator; + +@class LibboxPlatformInterface; + +@class LibboxProfilePreviewIterator; + +@class LibboxRoutePrefixIterator; + +@class LibboxStringIterator; + +@class LibboxTunInterface; + +@class LibboxTunOptions; + +@interface LibboxCommandClientHandler : NSObject <goSeqRefInterface, LibboxCommandClientHandler> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (void)clearLog; +- (void)connected; +- (void)disconnected:(NSString* _Nullable)message; +- (void)initializeClashMode:(id<LibboxStringIterator> _Nullable)modeList currentMode:(NSString* _Nullable)currentMode; +- (void)updateClashMode:(NSString* _Nullable)newMode; +- (void)writeGroups:(id<LibboxOutboundGroupIterator> _Nullable)message; +- (void)writeLog:(NSString* _Nullable)message; +- (void)writeStatus:(LibboxStatusMessage* _Nullable)message; +@end + +@interface LibboxCommandServerHandler : NSObject <goSeqRefInterface, LibboxCommandServerHandler> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (LibboxSystemProxyStatus* _Nullable)getSystemProxyStatus; +- (void)postServiceClose; +- (BOOL)serviceReload:(NSError* _Nullable* _Nullable)error; +- (BOOL)setSystemProxyEnabled:(BOOL)isEnabled error:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxFunc : NSObject <goSeqRefInterface, LibboxFunc> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)invoke:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxHTTPClient : NSObject <goSeqRefInterface, LibboxHTTPClient> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (void)close; +- (void)keepAlive; +- (void)modernTLS; +- (id<LibboxHTTPRequest> _Nullable)newRequest; +- (void)pinnedSHA256:(NSString* _Nullable)sumHex; +- (void)pinnedTLS12; +- (void)restrictedTLS; +- (void)trySocks5:(int32_t)port; +@end + +@interface LibboxHTTPRequest : NSObject <goSeqRefInterface, LibboxHTTPRequest> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (id<LibboxHTTPResponse> _Nullable)execute:(NSError* _Nullable* _Nullable)error; +- (void)randomUserAgent; +- (void)setContent:(NSData* _Nullable)content; +- (void)setContentString:(NSString* _Nullable)content; +- (void)setHeader:(NSString* _Nullable)key value:(NSString* _Nullable)value; +- (void)setMethod:(NSString* _Nullable)method; +- (BOOL)setURL:(NSString* _Nullable)link error:(NSError* _Nullable* _Nullable)error; +- (void)setUserAgent:(NSString* _Nullable)userAgent; +@end + +@interface LibboxHTTPResponse : NSObject <goSeqRefInterface, LibboxHTTPResponse> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (NSData* _Nullable)getContent:(NSError* _Nullable* _Nullable)error; +- (NSString* _Nonnull)getContentString:(NSError* _Nullable* _Nullable)error; +- (BOOL)writeTo:(NSString* _Nullable)path error:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxInterfaceUpdateListener : NSObject <goSeqRefInterface, LibboxInterfaceUpdateListener> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (void)updateDefaultInterface:(NSString* _Nullable)interfaceName interfaceIndex:(int32_t)interfaceIndex; +@end + +@interface LibboxLocalDNSTransport : NSObject <goSeqRefInterface, LibboxLocalDNSTransport> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)exchange:(LibboxExchangeContext* _Nullable)ctx message:(NSData* _Nullable)message error:(NSError* _Nullable* _Nullable)error; +- (BOOL)lookup:(LibboxExchangeContext* _Nullable)ctx network:(NSString* _Nullable)network domain:(NSString* _Nullable)domain error:(NSError* _Nullable* _Nullable)error; +- (BOOL)raw; +@end + +@interface LibboxNetworkInterfaceIterator : NSObject <goSeqRefInterface, LibboxNetworkInterfaceIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxNetworkInterface* _Nullable)next; +@end + +@interface LibboxOnDemandRule : NSObject <goSeqRefInterface, LibboxOnDemandRule> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (id<LibboxStringIterator> _Nullable)dnsSearchDomainMatch; +- (id<LibboxStringIterator> _Nullable)dnsServerAddressMatch; +- (int32_t)interfaceTypeMatch; +- (NSString* _Nonnull)probeURL; +- (id<LibboxStringIterator> _Nullable)ssidMatch; +- (int32_t)target; +@end + +@interface LibboxOnDemandRuleIterator : NSObject <goSeqRefInterface, LibboxOnDemandRuleIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (id<LibboxOnDemandRule> _Nullable)next; +@end + +@interface LibboxOutboundGroupItemIterator : NSObject <goSeqRefInterface, LibboxOutboundGroupItemIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxOutboundGroupItem* _Nullable)next; +@end + +@interface LibboxOutboundGroupIterator : NSObject <goSeqRefInterface, LibboxOutboundGroupIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxOutboundGroup* _Nullable)next; +@end + +@interface LibboxPlatformInterface : NSObject <goSeqRefInterface, LibboxPlatformInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)autoDetectInterfaceControl:(int32_t)fd error:(NSError* _Nullable* _Nullable)error; +- (void)clearDNSCache; +- (BOOL)closeDefaultInterfaceMonitor:(id<LibboxInterfaceUpdateListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; +- (BOOL)findConnectionOwner:(int32_t)ipProtocol sourceAddress:(NSString* _Nullable)sourceAddress sourcePort:(int32_t)sourcePort destinationAddress:(NSString* _Nullable)destinationAddress destinationPort:(int32_t)destinationPort ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (id<LibboxNetworkInterfaceIterator> _Nullable)getInterfaces:(NSError* _Nullable* _Nullable)error; +- (BOOL)includeAllNetworks; +- (BOOL)openTun:(id<LibboxTunOptions> _Nullable)options ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (NSString* _Nonnull)packageNameByUid:(int32_t)uid error:(NSError* _Nullable* _Nullable)error; +- (LibboxWIFIState* _Nullable)readWIFIState; +- (BOOL)startDefaultInterfaceMonitor:(id<LibboxInterfaceUpdateListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; +- (BOOL)uidByPackageName:(NSString* _Nullable)packageName ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (BOOL)underNetworkExtension; +- (BOOL)usePlatformAutoDetectInterfaceControl; +- (BOOL)usePlatformDefaultInterfaceMonitor; +- (BOOL)usePlatformInterfaceGetter; +- (BOOL)useProcFS; +- (void)writeLog:(NSString* _Nullable)message; +@end + +@interface LibboxProfilePreviewIterator : NSObject <goSeqRefInterface, LibboxProfilePreviewIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxProfilePreview* _Nullable)next; +@end + +@interface LibboxRoutePrefixIterator : NSObject <goSeqRefInterface, LibboxRoutePrefixIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxRoutePrefix* _Nullable)next; +@end + +@interface LibboxStringIterator : NSObject <goSeqRefInterface, LibboxStringIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (NSString* _Nonnull)next; +@end + +@interface LibboxTunInterface : NSObject <goSeqRefInterface, LibboxTunInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (int32_t)fileDescriptor; +@end + +@interface LibboxTunOptions : NSObject <goSeqRefInterface, LibboxTunOptions> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)getAutoRoute; +- (NSString* _Nonnull)getDNSServerAddress:(NSError* _Nullable* _Nullable)error; +- (id<LibboxStringIterator> _Nullable)getExcludePackage; +- (id<LibboxStringIterator> _Nullable)getHTTPProxyBypassDomain; +- (id<LibboxStringIterator> _Nullable)getHTTPProxyMatchDomain; +- (NSString* _Nonnull)getHTTPProxyServer; +- (int32_t)getHTTPProxyServerPort; +- (id<LibboxStringIterator> _Nullable)getIncludePackage; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4Address; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteExcludeAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteRange; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6Address; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteExcludeAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteRange; +- (int32_t)getMTU; +- (BOOL)getStrictRoute; +- (BOOL)isHTTPProxyEnabled; +@end + +#endif diff --git a/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Headers/Universe.objc.h b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Headers/Universe.objc.h new file mode 100644 index 0000000000000000000000000000000000000000..019e7502d581983722a15bf30799e85cbc5dd766 --- /dev/null +++ b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Headers/Universe.objc.h @@ -0,0 +1,29 @@ +// Objective-C API for talking to Go package. +// gobind -lang=objc +// +// File is generated by gobind. Do not edit. + +#ifndef __Universe_H__ +#define __Universe_H__ + +@import Foundation; +#include "ref.h" + +@protocol Universeerror; +@class Universeerror; + +@protocol Universeerror <NSObject> +- (NSString* _Nonnull)error; +@end + +@class Universeerror; + +@interface Universeerror : NSError <goSeqRefInterface, Universeerror> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (NSString* _Nonnull)error; +@end + +#endif diff --git a/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Headers/ref.h b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Headers/ref.h new file mode 100644 index 0000000000000000000000000000000000000000..b8036a4d85c7387f3def61473a071b5d8c4c8208 --- /dev/null +++ b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Headers/ref.h @@ -0,0 +1,35 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef __GO_REF_HDR__ +#define __GO_REF_HDR__ + +#include <Foundation/Foundation.h> + +// GoSeqRef is an object tagged with an integer for passing back and +// forth across the language boundary. A GoSeqRef may represent either +// an instance of a Go object, or an Objective-C object passed to Go. +// The explicit allocation of a GoSeqRef is used to pin a Go object +// when it is passed to Objective-C. The Go seq package maintains a +// reference to the Go object in a map keyed by the refnum along with +// a reference count. When the reference count reaches zero, the Go +// seq package will clear the corresponding entry in the map. +@interface GoSeqRef : NSObject { +} +@property(readonly) int32_t refnum; +@property(strong) id obj; // NULL when representing a Go object. + +// new GoSeqRef object to proxy a Go object. The refnum must be +// provided from Go side. +- (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj; + +- (int32_t)incNum; + +@end + +@protocol goSeqRefInterface +-(GoSeqRef*) _ref; +@end + +#endif diff --git a/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Libbox b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Libbox new file mode 100644 index 0000000000000000000000000000000000000000..c4bb880aec3c1566d970047d4fc476a19be17108 Binary files /dev/null and b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Libbox differ diff --git a/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Modules/module.modulemap b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Modules/module.modulemap new file mode 100644 index 0000000000000000000000000000000000000000..1ff5e29bdfd41d06429a16857384ad8cbffb85fe --- /dev/null +++ b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Modules/module.modulemap @@ -0,0 +1,8 @@ +framework module "Libbox" { + header "ref.h" + header "Libbox.objc.h" + header "Universe.objc.h" + header "Libbox.h" + + export * +} \ No newline at end of file diff --git a/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Resources/Info.plist b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Resources/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..0d1a4b8ab9b1fc8e9357197398f73353470cb636 --- /dev/null +++ b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/A/Resources/Info.plist @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> + <plist version="1.0"> + <dict> + </dict> + </plist> diff --git a/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/Current b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/Current new file mode 120000 index 0000000000000000000000000000000000000000..8c7e5a667f1b771847fe88c01c3de34413a1b220 --- /dev/null +++ b/Libbox.xcframework/macos-arm64_x86_64/Libbox.framework/Versions/Current @@ -0,0 +1 @@ +A \ No newline at end of file diff --git a/Libbox.xcframework/tvos-arm64/Libbox.framework/Headers b/Libbox.xcframework/tvos-arm64/Libbox.framework/Headers new file mode 120000 index 0000000000000000000000000000000000000000..a177d2a6b92600696030834c319f5e1434f9ee6a --- /dev/null +++ b/Libbox.xcframework/tvos-arm64/Libbox.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/Libbox.xcframework/tvos-arm64/Libbox.framework/Libbox b/Libbox.xcframework/tvos-arm64/Libbox.framework/Libbox new file mode 120000 index 0000000000000000000000000000000000000000..97ad87691145a5467708b9e044f9e0e4be79a763 --- /dev/null +++ b/Libbox.xcframework/tvos-arm64/Libbox.framework/Libbox @@ -0,0 +1 @@ +Versions/Current/Libbox \ No newline at end of file diff --git a/Libbox.xcframework/tvos-arm64/Libbox.framework/Modules b/Libbox.xcframework/tvos-arm64/Libbox.framework/Modules new file mode 120000 index 0000000000000000000000000000000000000000..5736f3186e797b8b787748c9979d0fed3b0536c3 --- /dev/null +++ b/Libbox.xcframework/tvos-arm64/Libbox.framework/Modules @@ -0,0 +1 @@ +Versions/Current/Modules \ No newline at end of file diff --git a/Libbox.xcframework/tvos-arm64/Libbox.framework/Resources b/Libbox.xcframework/tvos-arm64/Libbox.framework/Resources new file mode 120000 index 0000000000000000000000000000000000000000..953ee36f3bb709faf58a351e0b33c353e337c0a2 --- /dev/null +++ b/Libbox.xcframework/tvos-arm64/Libbox.framework/Resources @@ -0,0 +1 @@ +Versions/Current/Resources \ No newline at end of file diff --git a/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Headers/Libbox.h b/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Headers/Libbox.h new file mode 100644 index 0000000000000000000000000000000000000000..7e3aff43ac0a1cd946c152e9547907ff842adc70 --- /dev/null +++ b/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Headers/Libbox.h @@ -0,0 +1,13 @@ + +// Objective-C API for talking to the following Go packages +// +// github.com/sagernet/sing-box/experimental/libbox +// +// File is generated by gomobile bind. Do not edit. +#ifndef __Libbox_FRAMEWORK_H__ +#define __Libbox_FRAMEWORK_H__ + +#include "Libbox.objc.h" +#include "Universe.objc.h" + +#endif diff --git a/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Headers/Libbox.objc.h b/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Headers/Libbox.objc.h new file mode 100644 index 0000000000000000000000000000000000000000..98f33b9c58db878543752eb7027cc746d69a46fe --- /dev/null +++ b/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Headers/Libbox.objc.h @@ -0,0 +1,811 @@ +// Objective-C API for talking to github.com/sagernet/sing-box/experimental/libbox Go package. +// gobind -lang=objc github.com/sagernet/sing-box/experimental/libbox +// +// File is generated by gobind. Do not edit. + +#ifndef __Libbox_H__ +#define __Libbox_H__ + +@import Foundation; +#include "ref.h" +#include "Universe.objc.h" + + +@class LibboxBoxService; +@class LibboxCommandClient; +@class LibboxCommandClientOptions; +@class LibboxCommandServer; +@class LibboxErrorMessage; +@class LibboxExchangeContext; +@class LibboxImportRemoteProfile; +@class LibboxNetworkInterface; +@class LibboxOutboundGroup; +@class LibboxOutboundGroupItem; +@class LibboxPProfServer; +@class LibboxProfileContent; +@class LibboxProfileContentRequest; +@class LibboxProfileDecoder; +@class LibboxProfileEncoder; +@class LibboxProfilePreview; +@class LibboxRoutePrefix; +@class LibboxStatusMessage; +@class LibboxSystemProxyStatus; +@class LibboxWIFIState; +@protocol LibboxCommandClientHandler; +@class LibboxCommandClientHandler; +@protocol LibboxCommandServerHandler; +@class LibboxCommandServerHandler; +@protocol LibboxFunc; +@class LibboxFunc; +@protocol LibboxHTTPClient; +@class LibboxHTTPClient; +@protocol LibboxHTTPRequest; +@class LibboxHTTPRequest; +@protocol LibboxHTTPResponse; +@class LibboxHTTPResponse; +@protocol LibboxInterfaceUpdateListener; +@class LibboxInterfaceUpdateListener; +@protocol LibboxLocalDNSTransport; +@class LibboxLocalDNSTransport; +@protocol LibboxNetworkInterfaceIterator; +@class LibboxNetworkInterfaceIterator; +@protocol LibboxOnDemandRule; +@class LibboxOnDemandRule; +@protocol LibboxOnDemandRuleIterator; +@class LibboxOnDemandRuleIterator; +@protocol LibboxOutboundGroupItemIterator; +@class LibboxOutboundGroupItemIterator; +@protocol LibboxOutboundGroupIterator; +@class LibboxOutboundGroupIterator; +@protocol LibboxPlatformInterface; +@class LibboxPlatformInterface; +@protocol LibboxProfilePreviewIterator; +@class LibboxProfilePreviewIterator; +@protocol LibboxRoutePrefixIterator; +@class LibboxRoutePrefixIterator; +@protocol LibboxStringIterator; +@class LibboxStringIterator; +@protocol LibboxTunInterface; +@class LibboxTunInterface; +@protocol LibboxTunOptions; +@class LibboxTunOptions; + +@protocol LibboxCommandClientHandler <NSObject> +- (void)clearLog; +- (void)connected; +- (void)disconnected:(NSString* _Nullable)message; +- (void)initializeClashMode:(id<LibboxStringIterator> _Nullable)modeList currentMode:(NSString* _Nullable)currentMode; +- (void)updateClashMode:(NSString* _Nullable)newMode; +- (void)writeGroups:(id<LibboxOutboundGroupIterator> _Nullable)message; +- (void)writeLog:(NSString* _Nullable)message; +- (void)writeStatus:(LibboxStatusMessage* _Nullable)message; +@end + +@protocol LibboxCommandServerHandler <NSObject> +- (LibboxSystemProxyStatus* _Nullable)getSystemProxyStatus; +- (void)postServiceClose; +- (BOOL)serviceReload:(NSError* _Nullable* _Nullable)error; +- (BOOL)setSystemProxyEnabled:(BOOL)isEnabled error:(NSError* _Nullable* _Nullable)error; +@end + +@protocol LibboxFunc <NSObject> +- (BOOL)invoke:(NSError* _Nullable* _Nullable)error; +@end + +@protocol LibboxHTTPClient <NSObject> +- (void)close; +- (void)keepAlive; +- (void)modernTLS; +- (id<LibboxHTTPRequest> _Nullable)newRequest; +- (void)pinnedSHA256:(NSString* _Nullable)sumHex; +- (void)pinnedTLS12; +- (void)restrictedTLS; +- (void)trySocks5:(int32_t)port; +@end + +@protocol LibboxHTTPRequest <NSObject> +- (id<LibboxHTTPResponse> _Nullable)execute:(NSError* _Nullable* _Nullable)error; +- (void)randomUserAgent; +- (void)setContent:(NSData* _Nullable)content; +- (void)setContentString:(NSString* _Nullable)content; +- (void)setHeader:(NSString* _Nullable)key value:(NSString* _Nullable)value; +- (void)setMethod:(NSString* _Nullable)method; +- (BOOL)setURL:(NSString* _Nullable)link error:(NSError* _Nullable* _Nullable)error; +- (void)setUserAgent:(NSString* _Nullable)userAgent; +@end + +@protocol LibboxHTTPResponse <NSObject> +- (NSData* _Nullable)getContent:(NSError* _Nullable* _Nullable)error; +- (NSString* _Nonnull)getContentString:(NSError* _Nullable* _Nullable)error; +- (BOOL)writeTo:(NSString* _Nullable)path error:(NSError* _Nullable* _Nullable)error; +@end + +@protocol LibboxInterfaceUpdateListener <NSObject> +- (void)updateDefaultInterface:(NSString* _Nullable)interfaceName interfaceIndex:(int32_t)interfaceIndex; +@end + +@protocol LibboxLocalDNSTransport <NSObject> +- (BOOL)exchange:(LibboxExchangeContext* _Nullable)ctx message:(NSData* _Nullable)message error:(NSError* _Nullable* _Nullable)error; +- (BOOL)lookup:(LibboxExchangeContext* _Nullable)ctx network:(NSString* _Nullable)network domain:(NSString* _Nullable)domain error:(NSError* _Nullable* _Nullable)error; +- (BOOL)raw; +@end + +@protocol LibboxNetworkInterfaceIterator <NSObject> +- (BOOL)hasNext; +- (LibboxNetworkInterface* _Nullable)next; +@end + +@protocol LibboxOnDemandRule <NSObject> +- (id<LibboxStringIterator> _Nullable)dnsSearchDomainMatch; +- (id<LibboxStringIterator> _Nullable)dnsServerAddressMatch; +- (int32_t)interfaceTypeMatch; +- (NSString* _Nonnull)probeURL; +- (id<LibboxStringIterator> _Nullable)ssidMatch; +- (int32_t)target; +@end + +@protocol LibboxOnDemandRuleIterator <NSObject> +- (BOOL)hasNext; +- (id<LibboxOnDemandRule> _Nullable)next; +@end + +@protocol LibboxOutboundGroupItemIterator <NSObject> +- (BOOL)hasNext; +- (LibboxOutboundGroupItem* _Nullable)next; +@end + +@protocol LibboxOutboundGroupIterator <NSObject> +- (BOOL)hasNext; +- (LibboxOutboundGroup* _Nullable)next; +@end + +@protocol LibboxPlatformInterface <NSObject> +- (BOOL)autoDetectInterfaceControl:(int32_t)fd error:(NSError* _Nullable* _Nullable)error; +- (void)clearDNSCache; +- (BOOL)closeDefaultInterfaceMonitor:(id<LibboxInterfaceUpdateListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; +- (BOOL)findConnectionOwner:(int32_t)ipProtocol sourceAddress:(NSString* _Nullable)sourceAddress sourcePort:(int32_t)sourcePort destinationAddress:(NSString* _Nullable)destinationAddress destinationPort:(int32_t)destinationPort ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (id<LibboxNetworkInterfaceIterator> _Nullable)getInterfaces:(NSError* _Nullable* _Nullable)error; +- (BOOL)includeAllNetworks; +- (BOOL)openTun:(id<LibboxTunOptions> _Nullable)options ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (NSString* _Nonnull)packageNameByUid:(int32_t)uid error:(NSError* _Nullable* _Nullable)error; +- (LibboxWIFIState* _Nullable)readWIFIState; +- (BOOL)startDefaultInterfaceMonitor:(id<LibboxInterfaceUpdateListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; +- (BOOL)uidByPackageName:(NSString* _Nullable)packageName ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (BOOL)underNetworkExtension; +- (BOOL)usePlatformAutoDetectInterfaceControl; +- (BOOL)usePlatformDefaultInterfaceMonitor; +- (BOOL)usePlatformInterfaceGetter; +- (BOOL)useProcFS; +- (void)writeLog:(NSString* _Nullable)message; +@end + +@protocol LibboxProfilePreviewIterator <NSObject> +- (BOOL)hasNext; +- (LibboxProfilePreview* _Nullable)next; +@end + +@protocol LibboxRoutePrefixIterator <NSObject> +- (BOOL)hasNext; +- (LibboxRoutePrefix* _Nullable)next; +@end + +@protocol LibboxStringIterator <NSObject> +- (BOOL)hasNext; +- (NSString* _Nonnull)next; +@end + +@protocol LibboxTunInterface <NSObject> +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (int32_t)fileDescriptor; +@end + +@protocol LibboxTunOptions <NSObject> +- (BOOL)getAutoRoute; +- (NSString* _Nonnull)getDNSServerAddress:(NSError* _Nullable* _Nullable)error; +- (id<LibboxStringIterator> _Nullable)getExcludePackage; +- (id<LibboxStringIterator> _Nullable)getHTTPProxyBypassDomain; +- (id<LibboxStringIterator> _Nullable)getHTTPProxyMatchDomain; +- (NSString* _Nonnull)getHTTPProxyServer; +- (int32_t)getHTTPProxyServerPort; +- (id<LibboxStringIterator> _Nullable)getIncludePackage; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4Address; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteExcludeAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteRange; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6Address; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteExcludeAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteRange; +- (int32_t)getMTU; +- (BOOL)getStrictRoute; +- (BOOL)isHTTPProxyEnabled; +@end + +@interface LibboxBoxService : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (BOOL)needWIFIState; +- (void)pause; +- (BOOL)start:(NSError* _Nullable* _Nullable)error; +- (void)wake; +@end + +@interface LibboxCommandClient : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nullable instancetype)init:(id<LibboxCommandClientHandler> _Nullable)handler options:(LibboxCommandClientOptions* _Nullable)options; +- (BOOL)closeConnections:(NSError* _Nullable* _Nullable)error; +- (BOOL)connect:(NSError* _Nullable* _Nullable)error; +- (BOOL)disconnect:(NSError* _Nullable* _Nullable)error; +- (LibboxSystemProxyStatus* _Nullable)getSystemProxyStatus:(NSError* _Nullable* _Nullable)error; +- (BOOL)selectOutbound:(NSString* _Nullable)groupTag outboundTag:(NSString* _Nullable)outboundTag error:(NSError* _Nullable* _Nullable)error; +- (BOOL)serviceClose:(NSError* _Nullable* _Nullable)error; +- (BOOL)serviceReload:(NSError* _Nullable* _Nullable)error; +- (BOOL)setClashMode:(NSString* _Nullable)newMode error:(NSError* _Nullable* _Nullable)error; +- (BOOL)setGroupExpand:(NSString* _Nullable)groupTag isExpand:(BOOL)isExpand error:(NSError* _Nullable* _Nullable)error; +- (BOOL)setSystemProxyEnabled:(BOOL)isEnabled error:(NSError* _Nullable* _Nullable)error; +- (BOOL)urlTest:(NSString* _Nullable)groupTag error:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxCommandClientOptions : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int32_t command; +@property (nonatomic) int64_t statusInterval; +@end + +@interface LibboxCommandServer : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nullable instancetype)init:(id<LibboxCommandServerHandler> _Nullable)handler maxLines:(int32_t)maxLines; +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (void)resetLog; +- (void)setService:(LibboxBoxService* _Nullable)newService; +- (BOOL)start:(NSError* _Nullable* _Nullable)error; +- (void)writeMessage:(NSString* _Nullable)message; +@end + +@interface LibboxErrorMessage : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull message; +- (NSData* _Nullable)encode; +@end + +@interface LibboxExchangeContext : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (void)errnoCode:(int32_t)code; +- (void)errorCode:(int32_t)code; +- (void)onCancel:(id<LibboxFunc> _Nullable)callback; +- (void)rawSuccess:(NSData* _Nullable)result; +- (void)success:(NSString* _Nullable)result; +@end + +@interface LibboxImportRemoteProfile : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull name; +@property (nonatomic) NSString* _Nonnull url; +@property (nonatomic) NSString* _Nonnull host; +@end + +@interface LibboxNetworkInterface : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int32_t index; +@property (nonatomic) int32_t mtu; +@property (nonatomic) NSString* _Nonnull name; +@property (nonatomic) id<LibboxStringIterator> _Nullable addresses; +@end + +@interface LibboxOutboundGroup : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull tag; +@property (nonatomic) NSString* _Nonnull type; +@property (nonatomic) BOOL selectable; +@property (nonatomic) NSString* _Nonnull selected; +@property (nonatomic) BOOL isExpand; +- (id<LibboxOutboundGroupItemIterator> _Nullable)getItems; +@end + +@interface LibboxOutboundGroupItem : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull tag; +@property (nonatomic) NSString* _Nonnull type; +@property (nonatomic) int64_t urlTestTime; +@property (nonatomic) int32_t urlTestDelay; +@end + +@interface LibboxPProfServer : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nullable instancetype)init:(long)port; +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (BOOL)start:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxProfileContent : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull name; +@property (nonatomic) int32_t type; +@property (nonatomic) NSString* _Nonnull config; +@property (nonatomic) NSString* _Nonnull remotePath; +@property (nonatomic) BOOL autoUpdate; +@property (nonatomic) int32_t autoUpdateInterval; +@property (nonatomic) int64_t lastUpdated; +- (NSData* _Nullable)encode; +@end + +@interface LibboxProfileContentRequest : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int64_t profileID; +- (NSData* _Nullable)encode; +@end + +@interface LibboxProfileDecoder : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (BOOL)decode:(NSData* _Nullable)data error:(NSError* _Nullable* _Nullable)error; +- (id<LibboxProfilePreviewIterator> _Nullable)iterator; +@end + +@interface LibboxProfileEncoder : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (void)append:(LibboxProfilePreview* _Nullable)profile; +- (NSData* _Nullable)encode; +@end + +@interface LibboxProfilePreview : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int64_t profileID; +@property (nonatomic) NSString* _Nonnull name; +@property (nonatomic) int32_t type; +@end + +@interface LibboxRoutePrefix : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (NSString* _Nonnull)address; +- (NSString* _Nonnull)mask; +- (int32_t)prefix; +- (NSString* _Nonnull)string; +@end + +@interface LibboxStatusMessage : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int64_t memory; +@property (nonatomic) int32_t goroutines; +@property (nonatomic) int32_t connectionsIn; +@property (nonatomic) int32_t connectionsOut; +@property (nonatomic) BOOL trafficAvailable; +@property (nonatomic) int64_t uplink; +@property (nonatomic) int64_t downlink; +@property (nonatomic) int64_t uplinkTotal; +@property (nonatomic) int64_t downlinkTotal; +@end + +@interface LibboxSystemProxyStatus : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) BOOL available; +@property (nonatomic) BOOL enabled; +@end + +@interface LibboxWIFIState : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nullable instancetype)init:(NSString* _Nullable)wifiSSID wifiBSSID:(NSString* _Nullable)wifiBSSID; +@property (nonatomic) NSString* _Nonnull ssid; +@property (nonatomic) NSString* _Nonnull bssid; +@end + +FOUNDATION_EXPORT const int32_t LibboxCommandClashMode; +FOUNDATION_EXPORT const int32_t LibboxCommandCloseConnections; +FOUNDATION_EXPORT const int32_t LibboxCommandGetSystemProxyStatus; +FOUNDATION_EXPORT const int32_t LibboxCommandGroup; +FOUNDATION_EXPORT const int32_t LibboxCommandGroupExpand; +FOUNDATION_EXPORT const int32_t LibboxCommandLog; +FOUNDATION_EXPORT const int32_t LibboxCommandSelectOutbound; +FOUNDATION_EXPORT const int32_t LibboxCommandServiceClose; +FOUNDATION_EXPORT const int32_t LibboxCommandServiceReload; +FOUNDATION_EXPORT const int32_t LibboxCommandSetClashMode; +FOUNDATION_EXPORT const int32_t LibboxCommandSetSystemProxyEnabled; +FOUNDATION_EXPORT const int32_t LibboxCommandStatus; +FOUNDATION_EXPORT const int32_t LibboxCommandURLTest; +FOUNDATION_EXPORT const int64_t LibboxMessageTypeError; +FOUNDATION_EXPORT const int64_t LibboxMessageTypeProfileContent; +FOUNDATION_EXPORT const int64_t LibboxMessageTypeProfileContentRequest; +FOUNDATION_EXPORT const int64_t LibboxMessageTypeProfileList; +FOUNDATION_EXPORT const int32_t LibboxProfileTypeLocal; +FOUNDATION_EXPORT const int32_t LibboxProfileTypeRemote; +FOUNDATION_EXPORT const int32_t LibboxProfileTypeiCloud; + +FOUNDATION_EXPORT BOOL LibboxCheckConfig(NSString* _Nullable configContent, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT void LibboxClearServiceError(void); + +FOUNDATION_EXPORT LibboxErrorMessage* _Nullable LibboxDecodeErrorMessage(NSData* _Nullable data, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT int32_t LibboxDecodeLengthChunk(NSData* _Nullable data); + +FOUNDATION_EXPORT LibboxProfileContent* _Nullable LibboxDecodeProfileContent(NSData* _Nullable data, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT LibboxProfileContentRequest* _Nullable LibboxDecodeProfileContentRequest(NSData* _Nullable data, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT NSData* _Nullable LibboxEncodeChunkedMessage(NSData* _Nullable data); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxFormatBytes(int64_t length); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxFormatConfig(NSString* _Nullable configContent, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxFormatMemoryBytes(int64_t length); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxGenerateRemoteProfileImportLink(NSString* _Nullable name, NSString* _Nullable remoteURL); + +FOUNDATION_EXPORT int32_t LibboxGetTunnelFileDescriptor(void); + +FOUNDATION_EXPORT LibboxCommandClient* _Nullable LibboxNewCommandClient(id<LibboxCommandClientHandler> _Nullable handler, LibboxCommandClientOptions* _Nullable options); + +FOUNDATION_EXPORT LibboxCommandServer* _Nullable LibboxNewCommandServer(id<LibboxCommandServerHandler> _Nullable handler, int32_t maxLines); + +FOUNDATION_EXPORT id<LibboxHTTPClient> _Nullable LibboxNewHTTPClient(void); + +FOUNDATION_EXPORT LibboxPProfServer* _Nullable LibboxNewPProfServer(long port); + +FOUNDATION_EXPORT LibboxBoxService* _Nullable LibboxNewService(NSString* _Nullable configContent, id<LibboxPlatformInterface> _Nullable platformInterface, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT LibboxCommandClient* _Nullable LibboxNewStandaloneCommandClient(void); + +FOUNDATION_EXPORT LibboxWIFIState* _Nullable LibboxNewWIFIState(NSString* _Nullable wifiSSID, NSString* _Nullable wifiBSSID); + +FOUNDATION_EXPORT LibboxImportRemoteProfile* _Nullable LibboxParseRemoteProfileImportLink(NSString* _Nullable importLink, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxProxyDisplayType(NSString* _Nullable proxyType); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxReadServiceError(NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT BOOL LibboxRedirectStderr(NSString* _Nullable path, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT void LibboxRegisterLocalDNSTransport(id<LibboxLocalDNSTransport> _Nullable transport); + +FOUNDATION_EXPORT void LibboxSetMemoryLimit(BOOL enabled); + +FOUNDATION_EXPORT void LibboxSetup(NSString* _Nullable basePath, NSString* _Nullable workingPath, NSString* _Nullable tempPath, BOOL isTVOS); + +FOUNDATION_EXPORT BOOL LibboxSetupWithUsername(NSString* _Nullable basePath, NSString* _Nullable workingPath, NSString* _Nullable tempPath, NSString* _Nullable username, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxVersion(void); + +FOUNDATION_EXPORT BOOL LibboxWriteServiceError(NSString* _Nullable message, NSError* _Nullable* _Nullable error); + +@class LibboxCommandClientHandler; + +@class LibboxCommandServerHandler; + +@class LibboxFunc; + +@class LibboxHTTPClient; + +@class LibboxHTTPRequest; + +@class LibboxHTTPResponse; + +@class LibboxInterfaceUpdateListener; + +@class LibboxLocalDNSTransport; + +@class LibboxNetworkInterfaceIterator; + +@class LibboxOnDemandRule; + +@class LibboxOnDemandRuleIterator; + +@class LibboxOutboundGroupItemIterator; + +@class LibboxOutboundGroupIterator; + +@class LibboxPlatformInterface; + +@class LibboxProfilePreviewIterator; + +@class LibboxRoutePrefixIterator; + +@class LibboxStringIterator; + +@class LibboxTunInterface; + +@class LibboxTunOptions; + +@interface LibboxCommandClientHandler : NSObject <goSeqRefInterface, LibboxCommandClientHandler> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (void)clearLog; +- (void)connected; +- (void)disconnected:(NSString* _Nullable)message; +- (void)initializeClashMode:(id<LibboxStringIterator> _Nullable)modeList currentMode:(NSString* _Nullable)currentMode; +- (void)updateClashMode:(NSString* _Nullable)newMode; +- (void)writeGroups:(id<LibboxOutboundGroupIterator> _Nullable)message; +- (void)writeLog:(NSString* _Nullable)message; +- (void)writeStatus:(LibboxStatusMessage* _Nullable)message; +@end + +@interface LibboxCommandServerHandler : NSObject <goSeqRefInterface, LibboxCommandServerHandler> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (LibboxSystemProxyStatus* _Nullable)getSystemProxyStatus; +- (void)postServiceClose; +- (BOOL)serviceReload:(NSError* _Nullable* _Nullable)error; +- (BOOL)setSystemProxyEnabled:(BOOL)isEnabled error:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxFunc : NSObject <goSeqRefInterface, LibboxFunc> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)invoke:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxHTTPClient : NSObject <goSeqRefInterface, LibboxHTTPClient> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (void)close; +- (void)keepAlive; +- (void)modernTLS; +- (id<LibboxHTTPRequest> _Nullable)newRequest; +- (void)pinnedSHA256:(NSString* _Nullable)sumHex; +- (void)pinnedTLS12; +- (void)restrictedTLS; +- (void)trySocks5:(int32_t)port; +@end + +@interface LibboxHTTPRequest : NSObject <goSeqRefInterface, LibboxHTTPRequest> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (id<LibboxHTTPResponse> _Nullable)execute:(NSError* _Nullable* _Nullable)error; +- (void)randomUserAgent; +- (void)setContent:(NSData* _Nullable)content; +- (void)setContentString:(NSString* _Nullable)content; +- (void)setHeader:(NSString* _Nullable)key value:(NSString* _Nullable)value; +- (void)setMethod:(NSString* _Nullable)method; +- (BOOL)setURL:(NSString* _Nullable)link error:(NSError* _Nullable* _Nullable)error; +- (void)setUserAgent:(NSString* _Nullable)userAgent; +@end + +@interface LibboxHTTPResponse : NSObject <goSeqRefInterface, LibboxHTTPResponse> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (NSData* _Nullable)getContent:(NSError* _Nullable* _Nullable)error; +- (NSString* _Nonnull)getContentString:(NSError* _Nullable* _Nullable)error; +- (BOOL)writeTo:(NSString* _Nullable)path error:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxInterfaceUpdateListener : NSObject <goSeqRefInterface, LibboxInterfaceUpdateListener> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (void)updateDefaultInterface:(NSString* _Nullable)interfaceName interfaceIndex:(int32_t)interfaceIndex; +@end + +@interface LibboxLocalDNSTransport : NSObject <goSeqRefInterface, LibboxLocalDNSTransport> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)exchange:(LibboxExchangeContext* _Nullable)ctx message:(NSData* _Nullable)message error:(NSError* _Nullable* _Nullable)error; +- (BOOL)lookup:(LibboxExchangeContext* _Nullable)ctx network:(NSString* _Nullable)network domain:(NSString* _Nullable)domain error:(NSError* _Nullable* _Nullable)error; +- (BOOL)raw; +@end + +@interface LibboxNetworkInterfaceIterator : NSObject <goSeqRefInterface, LibboxNetworkInterfaceIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxNetworkInterface* _Nullable)next; +@end + +@interface LibboxOnDemandRule : NSObject <goSeqRefInterface, LibboxOnDemandRule> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (id<LibboxStringIterator> _Nullable)dnsSearchDomainMatch; +- (id<LibboxStringIterator> _Nullable)dnsServerAddressMatch; +- (int32_t)interfaceTypeMatch; +- (NSString* _Nonnull)probeURL; +- (id<LibboxStringIterator> _Nullable)ssidMatch; +- (int32_t)target; +@end + +@interface LibboxOnDemandRuleIterator : NSObject <goSeqRefInterface, LibboxOnDemandRuleIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (id<LibboxOnDemandRule> _Nullable)next; +@end + +@interface LibboxOutboundGroupItemIterator : NSObject <goSeqRefInterface, LibboxOutboundGroupItemIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxOutboundGroupItem* _Nullable)next; +@end + +@interface LibboxOutboundGroupIterator : NSObject <goSeqRefInterface, LibboxOutboundGroupIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxOutboundGroup* _Nullable)next; +@end + +@interface LibboxPlatformInterface : NSObject <goSeqRefInterface, LibboxPlatformInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)autoDetectInterfaceControl:(int32_t)fd error:(NSError* _Nullable* _Nullable)error; +- (void)clearDNSCache; +- (BOOL)closeDefaultInterfaceMonitor:(id<LibboxInterfaceUpdateListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; +- (BOOL)findConnectionOwner:(int32_t)ipProtocol sourceAddress:(NSString* _Nullable)sourceAddress sourcePort:(int32_t)sourcePort destinationAddress:(NSString* _Nullable)destinationAddress destinationPort:(int32_t)destinationPort ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (id<LibboxNetworkInterfaceIterator> _Nullable)getInterfaces:(NSError* _Nullable* _Nullable)error; +- (BOOL)includeAllNetworks; +- (BOOL)openTun:(id<LibboxTunOptions> _Nullable)options ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (NSString* _Nonnull)packageNameByUid:(int32_t)uid error:(NSError* _Nullable* _Nullable)error; +- (LibboxWIFIState* _Nullable)readWIFIState; +- (BOOL)startDefaultInterfaceMonitor:(id<LibboxInterfaceUpdateListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; +- (BOOL)uidByPackageName:(NSString* _Nullable)packageName ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (BOOL)underNetworkExtension; +- (BOOL)usePlatformAutoDetectInterfaceControl; +- (BOOL)usePlatformDefaultInterfaceMonitor; +- (BOOL)usePlatformInterfaceGetter; +- (BOOL)useProcFS; +- (void)writeLog:(NSString* _Nullable)message; +@end + +@interface LibboxProfilePreviewIterator : NSObject <goSeqRefInterface, LibboxProfilePreviewIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxProfilePreview* _Nullable)next; +@end + +@interface LibboxRoutePrefixIterator : NSObject <goSeqRefInterface, LibboxRoutePrefixIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxRoutePrefix* _Nullable)next; +@end + +@interface LibboxStringIterator : NSObject <goSeqRefInterface, LibboxStringIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (NSString* _Nonnull)next; +@end + +@interface LibboxTunInterface : NSObject <goSeqRefInterface, LibboxTunInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (int32_t)fileDescriptor; +@end + +@interface LibboxTunOptions : NSObject <goSeqRefInterface, LibboxTunOptions> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)getAutoRoute; +- (NSString* _Nonnull)getDNSServerAddress:(NSError* _Nullable* _Nullable)error; +- (id<LibboxStringIterator> _Nullable)getExcludePackage; +- (id<LibboxStringIterator> _Nullable)getHTTPProxyBypassDomain; +- (id<LibboxStringIterator> _Nullable)getHTTPProxyMatchDomain; +- (NSString* _Nonnull)getHTTPProxyServer; +- (int32_t)getHTTPProxyServerPort; +- (id<LibboxStringIterator> _Nullable)getIncludePackage; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4Address; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteExcludeAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteRange; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6Address; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteExcludeAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteRange; +- (int32_t)getMTU; +- (BOOL)getStrictRoute; +- (BOOL)isHTTPProxyEnabled; +@end + +#endif diff --git a/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Headers/Universe.objc.h b/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Headers/Universe.objc.h new file mode 100644 index 0000000000000000000000000000000000000000..019e7502d581983722a15bf30799e85cbc5dd766 --- /dev/null +++ b/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Headers/Universe.objc.h @@ -0,0 +1,29 @@ +// Objective-C API for talking to Go package. +// gobind -lang=objc +// +// File is generated by gobind. Do not edit. + +#ifndef __Universe_H__ +#define __Universe_H__ + +@import Foundation; +#include "ref.h" + +@protocol Universeerror; +@class Universeerror; + +@protocol Universeerror <NSObject> +- (NSString* _Nonnull)error; +@end + +@class Universeerror; + +@interface Universeerror : NSError <goSeqRefInterface, Universeerror> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (NSString* _Nonnull)error; +@end + +#endif diff --git a/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Headers/ref.h b/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Headers/ref.h new file mode 100644 index 0000000000000000000000000000000000000000..b8036a4d85c7387f3def61473a071b5d8c4c8208 --- /dev/null +++ b/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Headers/ref.h @@ -0,0 +1,35 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef __GO_REF_HDR__ +#define __GO_REF_HDR__ + +#include <Foundation/Foundation.h> + +// GoSeqRef is an object tagged with an integer for passing back and +// forth across the language boundary. A GoSeqRef may represent either +// an instance of a Go object, or an Objective-C object passed to Go. +// The explicit allocation of a GoSeqRef is used to pin a Go object +// when it is passed to Objective-C. The Go seq package maintains a +// reference to the Go object in a map keyed by the refnum along with +// a reference count. When the reference count reaches zero, the Go +// seq package will clear the corresponding entry in the map. +@interface GoSeqRef : NSObject { +} +@property(readonly) int32_t refnum; +@property(strong) id obj; // NULL when representing a Go object. + +// new GoSeqRef object to proxy a Go object. The refnum must be +// provided from Go side. +- (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj; + +- (int32_t)incNum; + +@end + +@protocol goSeqRefInterface +-(GoSeqRef*) _ref; +@end + +#endif diff --git a/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Libbox b/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Libbox new file mode 100644 index 0000000000000000000000000000000000000000..8ed4612b003dd04e37fe12aeefb264ced4fc2aa7 Binary files /dev/null and b/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Libbox differ diff --git a/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Modules/module.modulemap b/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Modules/module.modulemap new file mode 100644 index 0000000000000000000000000000000000000000..1ff5e29bdfd41d06429a16857384ad8cbffb85fe --- /dev/null +++ b/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Modules/module.modulemap @@ -0,0 +1,8 @@ +framework module "Libbox" { + header "ref.h" + header "Libbox.objc.h" + header "Universe.objc.h" + header "Libbox.h" + + export * +} \ No newline at end of file diff --git a/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Resources/Info.plist b/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Resources/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..0d1a4b8ab9b1fc8e9357197398f73353470cb636 --- /dev/null +++ b/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/A/Resources/Info.plist @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> + <plist version="1.0"> + <dict> + </dict> + </plist> diff --git a/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/Current b/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/Current new file mode 120000 index 0000000000000000000000000000000000000000..8c7e5a667f1b771847fe88c01c3de34413a1b220 --- /dev/null +++ b/Libbox.xcframework/tvos-arm64/Libbox.framework/Versions/Current @@ -0,0 +1 @@ +A \ No newline at end of file diff --git a/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Headers b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Headers new file mode 120000 index 0000000000000000000000000000000000000000..a177d2a6b92600696030834c319f5e1434f9ee6a --- /dev/null +++ b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Libbox b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Libbox new file mode 120000 index 0000000000000000000000000000000000000000..97ad87691145a5467708b9e044f9e0e4be79a763 --- /dev/null +++ b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Libbox @@ -0,0 +1 @@ +Versions/Current/Libbox \ No newline at end of file diff --git a/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Modules b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Modules new file mode 120000 index 0000000000000000000000000000000000000000..5736f3186e797b8b787748c9979d0fed3b0536c3 --- /dev/null +++ b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Modules @@ -0,0 +1 @@ +Versions/Current/Modules \ No newline at end of file diff --git a/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Resources b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Resources new file mode 120000 index 0000000000000000000000000000000000000000..953ee36f3bb709faf58a351e0b33c353e337c0a2 --- /dev/null +++ b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Resources @@ -0,0 +1 @@ +Versions/Current/Resources \ No newline at end of file diff --git a/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/Libbox.h b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/Libbox.h new file mode 100644 index 0000000000000000000000000000000000000000..7e3aff43ac0a1cd946c152e9547907ff842adc70 --- /dev/null +++ b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/Libbox.h @@ -0,0 +1,13 @@ + +// Objective-C API for talking to the following Go packages +// +// github.com/sagernet/sing-box/experimental/libbox +// +// File is generated by gomobile bind. Do not edit. +#ifndef __Libbox_FRAMEWORK_H__ +#define __Libbox_FRAMEWORK_H__ + +#include "Libbox.objc.h" +#include "Universe.objc.h" + +#endif diff --git a/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/Libbox.objc.h b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/Libbox.objc.h new file mode 100644 index 0000000000000000000000000000000000000000..98f33b9c58db878543752eb7027cc746d69a46fe --- /dev/null +++ b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/Libbox.objc.h @@ -0,0 +1,811 @@ +// Objective-C API for talking to github.com/sagernet/sing-box/experimental/libbox Go package. +// gobind -lang=objc github.com/sagernet/sing-box/experimental/libbox +// +// File is generated by gobind. Do not edit. + +#ifndef __Libbox_H__ +#define __Libbox_H__ + +@import Foundation; +#include "ref.h" +#include "Universe.objc.h" + + +@class LibboxBoxService; +@class LibboxCommandClient; +@class LibboxCommandClientOptions; +@class LibboxCommandServer; +@class LibboxErrorMessage; +@class LibboxExchangeContext; +@class LibboxImportRemoteProfile; +@class LibboxNetworkInterface; +@class LibboxOutboundGroup; +@class LibboxOutboundGroupItem; +@class LibboxPProfServer; +@class LibboxProfileContent; +@class LibboxProfileContentRequest; +@class LibboxProfileDecoder; +@class LibboxProfileEncoder; +@class LibboxProfilePreview; +@class LibboxRoutePrefix; +@class LibboxStatusMessage; +@class LibboxSystemProxyStatus; +@class LibboxWIFIState; +@protocol LibboxCommandClientHandler; +@class LibboxCommandClientHandler; +@protocol LibboxCommandServerHandler; +@class LibboxCommandServerHandler; +@protocol LibboxFunc; +@class LibboxFunc; +@protocol LibboxHTTPClient; +@class LibboxHTTPClient; +@protocol LibboxHTTPRequest; +@class LibboxHTTPRequest; +@protocol LibboxHTTPResponse; +@class LibboxHTTPResponse; +@protocol LibboxInterfaceUpdateListener; +@class LibboxInterfaceUpdateListener; +@protocol LibboxLocalDNSTransport; +@class LibboxLocalDNSTransport; +@protocol LibboxNetworkInterfaceIterator; +@class LibboxNetworkInterfaceIterator; +@protocol LibboxOnDemandRule; +@class LibboxOnDemandRule; +@protocol LibboxOnDemandRuleIterator; +@class LibboxOnDemandRuleIterator; +@protocol LibboxOutboundGroupItemIterator; +@class LibboxOutboundGroupItemIterator; +@protocol LibboxOutboundGroupIterator; +@class LibboxOutboundGroupIterator; +@protocol LibboxPlatformInterface; +@class LibboxPlatformInterface; +@protocol LibboxProfilePreviewIterator; +@class LibboxProfilePreviewIterator; +@protocol LibboxRoutePrefixIterator; +@class LibboxRoutePrefixIterator; +@protocol LibboxStringIterator; +@class LibboxStringIterator; +@protocol LibboxTunInterface; +@class LibboxTunInterface; +@protocol LibboxTunOptions; +@class LibboxTunOptions; + +@protocol LibboxCommandClientHandler <NSObject> +- (void)clearLog; +- (void)connected; +- (void)disconnected:(NSString* _Nullable)message; +- (void)initializeClashMode:(id<LibboxStringIterator> _Nullable)modeList currentMode:(NSString* _Nullable)currentMode; +- (void)updateClashMode:(NSString* _Nullable)newMode; +- (void)writeGroups:(id<LibboxOutboundGroupIterator> _Nullable)message; +- (void)writeLog:(NSString* _Nullable)message; +- (void)writeStatus:(LibboxStatusMessage* _Nullable)message; +@end + +@protocol LibboxCommandServerHandler <NSObject> +- (LibboxSystemProxyStatus* _Nullable)getSystemProxyStatus; +- (void)postServiceClose; +- (BOOL)serviceReload:(NSError* _Nullable* _Nullable)error; +- (BOOL)setSystemProxyEnabled:(BOOL)isEnabled error:(NSError* _Nullable* _Nullable)error; +@end + +@protocol LibboxFunc <NSObject> +- (BOOL)invoke:(NSError* _Nullable* _Nullable)error; +@end + +@protocol LibboxHTTPClient <NSObject> +- (void)close; +- (void)keepAlive; +- (void)modernTLS; +- (id<LibboxHTTPRequest> _Nullable)newRequest; +- (void)pinnedSHA256:(NSString* _Nullable)sumHex; +- (void)pinnedTLS12; +- (void)restrictedTLS; +- (void)trySocks5:(int32_t)port; +@end + +@protocol LibboxHTTPRequest <NSObject> +- (id<LibboxHTTPResponse> _Nullable)execute:(NSError* _Nullable* _Nullable)error; +- (void)randomUserAgent; +- (void)setContent:(NSData* _Nullable)content; +- (void)setContentString:(NSString* _Nullable)content; +- (void)setHeader:(NSString* _Nullable)key value:(NSString* _Nullable)value; +- (void)setMethod:(NSString* _Nullable)method; +- (BOOL)setURL:(NSString* _Nullable)link error:(NSError* _Nullable* _Nullable)error; +- (void)setUserAgent:(NSString* _Nullable)userAgent; +@end + +@protocol LibboxHTTPResponse <NSObject> +- (NSData* _Nullable)getContent:(NSError* _Nullable* _Nullable)error; +- (NSString* _Nonnull)getContentString:(NSError* _Nullable* _Nullable)error; +- (BOOL)writeTo:(NSString* _Nullable)path error:(NSError* _Nullable* _Nullable)error; +@end + +@protocol LibboxInterfaceUpdateListener <NSObject> +- (void)updateDefaultInterface:(NSString* _Nullable)interfaceName interfaceIndex:(int32_t)interfaceIndex; +@end + +@protocol LibboxLocalDNSTransport <NSObject> +- (BOOL)exchange:(LibboxExchangeContext* _Nullable)ctx message:(NSData* _Nullable)message error:(NSError* _Nullable* _Nullable)error; +- (BOOL)lookup:(LibboxExchangeContext* _Nullable)ctx network:(NSString* _Nullable)network domain:(NSString* _Nullable)domain error:(NSError* _Nullable* _Nullable)error; +- (BOOL)raw; +@end + +@protocol LibboxNetworkInterfaceIterator <NSObject> +- (BOOL)hasNext; +- (LibboxNetworkInterface* _Nullable)next; +@end + +@protocol LibboxOnDemandRule <NSObject> +- (id<LibboxStringIterator> _Nullable)dnsSearchDomainMatch; +- (id<LibboxStringIterator> _Nullable)dnsServerAddressMatch; +- (int32_t)interfaceTypeMatch; +- (NSString* _Nonnull)probeURL; +- (id<LibboxStringIterator> _Nullable)ssidMatch; +- (int32_t)target; +@end + +@protocol LibboxOnDemandRuleIterator <NSObject> +- (BOOL)hasNext; +- (id<LibboxOnDemandRule> _Nullable)next; +@end + +@protocol LibboxOutboundGroupItemIterator <NSObject> +- (BOOL)hasNext; +- (LibboxOutboundGroupItem* _Nullable)next; +@end + +@protocol LibboxOutboundGroupIterator <NSObject> +- (BOOL)hasNext; +- (LibboxOutboundGroup* _Nullable)next; +@end + +@protocol LibboxPlatformInterface <NSObject> +- (BOOL)autoDetectInterfaceControl:(int32_t)fd error:(NSError* _Nullable* _Nullable)error; +- (void)clearDNSCache; +- (BOOL)closeDefaultInterfaceMonitor:(id<LibboxInterfaceUpdateListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; +- (BOOL)findConnectionOwner:(int32_t)ipProtocol sourceAddress:(NSString* _Nullable)sourceAddress sourcePort:(int32_t)sourcePort destinationAddress:(NSString* _Nullable)destinationAddress destinationPort:(int32_t)destinationPort ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (id<LibboxNetworkInterfaceIterator> _Nullable)getInterfaces:(NSError* _Nullable* _Nullable)error; +- (BOOL)includeAllNetworks; +- (BOOL)openTun:(id<LibboxTunOptions> _Nullable)options ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (NSString* _Nonnull)packageNameByUid:(int32_t)uid error:(NSError* _Nullable* _Nullable)error; +- (LibboxWIFIState* _Nullable)readWIFIState; +- (BOOL)startDefaultInterfaceMonitor:(id<LibboxInterfaceUpdateListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; +- (BOOL)uidByPackageName:(NSString* _Nullable)packageName ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (BOOL)underNetworkExtension; +- (BOOL)usePlatformAutoDetectInterfaceControl; +- (BOOL)usePlatformDefaultInterfaceMonitor; +- (BOOL)usePlatformInterfaceGetter; +- (BOOL)useProcFS; +- (void)writeLog:(NSString* _Nullable)message; +@end + +@protocol LibboxProfilePreviewIterator <NSObject> +- (BOOL)hasNext; +- (LibboxProfilePreview* _Nullable)next; +@end + +@protocol LibboxRoutePrefixIterator <NSObject> +- (BOOL)hasNext; +- (LibboxRoutePrefix* _Nullable)next; +@end + +@protocol LibboxStringIterator <NSObject> +- (BOOL)hasNext; +- (NSString* _Nonnull)next; +@end + +@protocol LibboxTunInterface <NSObject> +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (int32_t)fileDescriptor; +@end + +@protocol LibboxTunOptions <NSObject> +- (BOOL)getAutoRoute; +- (NSString* _Nonnull)getDNSServerAddress:(NSError* _Nullable* _Nullable)error; +- (id<LibboxStringIterator> _Nullable)getExcludePackage; +- (id<LibboxStringIterator> _Nullable)getHTTPProxyBypassDomain; +- (id<LibboxStringIterator> _Nullable)getHTTPProxyMatchDomain; +- (NSString* _Nonnull)getHTTPProxyServer; +- (int32_t)getHTTPProxyServerPort; +- (id<LibboxStringIterator> _Nullable)getIncludePackage; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4Address; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteExcludeAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteRange; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6Address; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteExcludeAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteRange; +- (int32_t)getMTU; +- (BOOL)getStrictRoute; +- (BOOL)isHTTPProxyEnabled; +@end + +@interface LibboxBoxService : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (BOOL)needWIFIState; +- (void)pause; +- (BOOL)start:(NSError* _Nullable* _Nullable)error; +- (void)wake; +@end + +@interface LibboxCommandClient : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nullable instancetype)init:(id<LibboxCommandClientHandler> _Nullable)handler options:(LibboxCommandClientOptions* _Nullable)options; +- (BOOL)closeConnections:(NSError* _Nullable* _Nullable)error; +- (BOOL)connect:(NSError* _Nullable* _Nullable)error; +- (BOOL)disconnect:(NSError* _Nullable* _Nullable)error; +- (LibboxSystemProxyStatus* _Nullable)getSystemProxyStatus:(NSError* _Nullable* _Nullable)error; +- (BOOL)selectOutbound:(NSString* _Nullable)groupTag outboundTag:(NSString* _Nullable)outboundTag error:(NSError* _Nullable* _Nullable)error; +- (BOOL)serviceClose:(NSError* _Nullable* _Nullable)error; +- (BOOL)serviceReload:(NSError* _Nullable* _Nullable)error; +- (BOOL)setClashMode:(NSString* _Nullable)newMode error:(NSError* _Nullable* _Nullable)error; +- (BOOL)setGroupExpand:(NSString* _Nullable)groupTag isExpand:(BOOL)isExpand error:(NSError* _Nullable* _Nullable)error; +- (BOOL)setSystemProxyEnabled:(BOOL)isEnabled error:(NSError* _Nullable* _Nullable)error; +- (BOOL)urlTest:(NSString* _Nullable)groupTag error:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxCommandClientOptions : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int32_t command; +@property (nonatomic) int64_t statusInterval; +@end + +@interface LibboxCommandServer : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nullable instancetype)init:(id<LibboxCommandServerHandler> _Nullable)handler maxLines:(int32_t)maxLines; +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (void)resetLog; +- (void)setService:(LibboxBoxService* _Nullable)newService; +- (BOOL)start:(NSError* _Nullable* _Nullable)error; +- (void)writeMessage:(NSString* _Nullable)message; +@end + +@interface LibboxErrorMessage : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull message; +- (NSData* _Nullable)encode; +@end + +@interface LibboxExchangeContext : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (void)errnoCode:(int32_t)code; +- (void)errorCode:(int32_t)code; +- (void)onCancel:(id<LibboxFunc> _Nullable)callback; +- (void)rawSuccess:(NSData* _Nullable)result; +- (void)success:(NSString* _Nullable)result; +@end + +@interface LibboxImportRemoteProfile : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull name; +@property (nonatomic) NSString* _Nonnull url; +@property (nonatomic) NSString* _Nonnull host; +@end + +@interface LibboxNetworkInterface : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int32_t index; +@property (nonatomic) int32_t mtu; +@property (nonatomic) NSString* _Nonnull name; +@property (nonatomic) id<LibboxStringIterator> _Nullable addresses; +@end + +@interface LibboxOutboundGroup : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull tag; +@property (nonatomic) NSString* _Nonnull type; +@property (nonatomic) BOOL selectable; +@property (nonatomic) NSString* _Nonnull selected; +@property (nonatomic) BOOL isExpand; +- (id<LibboxOutboundGroupItemIterator> _Nullable)getItems; +@end + +@interface LibboxOutboundGroupItem : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull tag; +@property (nonatomic) NSString* _Nonnull type; +@property (nonatomic) int64_t urlTestTime; +@property (nonatomic) int32_t urlTestDelay; +@end + +@interface LibboxPProfServer : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nullable instancetype)init:(long)port; +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (BOOL)start:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxProfileContent : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) NSString* _Nonnull name; +@property (nonatomic) int32_t type; +@property (nonatomic) NSString* _Nonnull config; +@property (nonatomic) NSString* _Nonnull remotePath; +@property (nonatomic) BOOL autoUpdate; +@property (nonatomic) int32_t autoUpdateInterval; +@property (nonatomic) int64_t lastUpdated; +- (NSData* _Nullable)encode; +@end + +@interface LibboxProfileContentRequest : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int64_t profileID; +- (NSData* _Nullable)encode; +@end + +@interface LibboxProfileDecoder : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (BOOL)decode:(NSData* _Nullable)data error:(NSError* _Nullable* _Nullable)error; +- (id<LibboxProfilePreviewIterator> _Nullable)iterator; +@end + +@interface LibboxProfileEncoder : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (void)append:(LibboxProfilePreview* _Nullable)profile; +- (NSData* _Nullable)encode; +@end + +@interface LibboxProfilePreview : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int64_t profileID; +@property (nonatomic) NSString* _Nonnull name; +@property (nonatomic) int32_t type; +@end + +@interface LibboxRoutePrefix : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +- (NSString* _Nonnull)address; +- (NSString* _Nonnull)mask; +- (int32_t)prefix; +- (NSString* _Nonnull)string; +@end + +@interface LibboxStatusMessage : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) int64_t memory; +@property (nonatomic) int32_t goroutines; +@property (nonatomic) int32_t connectionsIn; +@property (nonatomic) int32_t connectionsOut; +@property (nonatomic) BOOL trafficAvailable; +@property (nonatomic) int64_t uplink; +@property (nonatomic) int64_t downlink; +@property (nonatomic) int64_t uplinkTotal; +@property (nonatomic) int64_t downlinkTotal; +@end + +@interface LibboxSystemProxyStatus : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nonnull instancetype)init; +@property (nonatomic) BOOL available; +@property (nonatomic) BOOL enabled; +@end + +@interface LibboxWIFIState : NSObject <goSeqRefInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (nullable instancetype)init:(NSString* _Nullable)wifiSSID wifiBSSID:(NSString* _Nullable)wifiBSSID; +@property (nonatomic) NSString* _Nonnull ssid; +@property (nonatomic) NSString* _Nonnull bssid; +@end + +FOUNDATION_EXPORT const int32_t LibboxCommandClashMode; +FOUNDATION_EXPORT const int32_t LibboxCommandCloseConnections; +FOUNDATION_EXPORT const int32_t LibboxCommandGetSystemProxyStatus; +FOUNDATION_EXPORT const int32_t LibboxCommandGroup; +FOUNDATION_EXPORT const int32_t LibboxCommandGroupExpand; +FOUNDATION_EXPORT const int32_t LibboxCommandLog; +FOUNDATION_EXPORT const int32_t LibboxCommandSelectOutbound; +FOUNDATION_EXPORT const int32_t LibboxCommandServiceClose; +FOUNDATION_EXPORT const int32_t LibboxCommandServiceReload; +FOUNDATION_EXPORT const int32_t LibboxCommandSetClashMode; +FOUNDATION_EXPORT const int32_t LibboxCommandSetSystemProxyEnabled; +FOUNDATION_EXPORT const int32_t LibboxCommandStatus; +FOUNDATION_EXPORT const int32_t LibboxCommandURLTest; +FOUNDATION_EXPORT const int64_t LibboxMessageTypeError; +FOUNDATION_EXPORT const int64_t LibboxMessageTypeProfileContent; +FOUNDATION_EXPORT const int64_t LibboxMessageTypeProfileContentRequest; +FOUNDATION_EXPORT const int64_t LibboxMessageTypeProfileList; +FOUNDATION_EXPORT const int32_t LibboxProfileTypeLocal; +FOUNDATION_EXPORT const int32_t LibboxProfileTypeRemote; +FOUNDATION_EXPORT const int32_t LibboxProfileTypeiCloud; + +FOUNDATION_EXPORT BOOL LibboxCheckConfig(NSString* _Nullable configContent, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT void LibboxClearServiceError(void); + +FOUNDATION_EXPORT LibboxErrorMessage* _Nullable LibboxDecodeErrorMessage(NSData* _Nullable data, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT int32_t LibboxDecodeLengthChunk(NSData* _Nullable data); + +FOUNDATION_EXPORT LibboxProfileContent* _Nullable LibboxDecodeProfileContent(NSData* _Nullable data, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT LibboxProfileContentRequest* _Nullable LibboxDecodeProfileContentRequest(NSData* _Nullable data, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT NSData* _Nullable LibboxEncodeChunkedMessage(NSData* _Nullable data); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxFormatBytes(int64_t length); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxFormatConfig(NSString* _Nullable configContent, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxFormatMemoryBytes(int64_t length); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxGenerateRemoteProfileImportLink(NSString* _Nullable name, NSString* _Nullable remoteURL); + +FOUNDATION_EXPORT int32_t LibboxGetTunnelFileDescriptor(void); + +FOUNDATION_EXPORT LibboxCommandClient* _Nullable LibboxNewCommandClient(id<LibboxCommandClientHandler> _Nullable handler, LibboxCommandClientOptions* _Nullable options); + +FOUNDATION_EXPORT LibboxCommandServer* _Nullable LibboxNewCommandServer(id<LibboxCommandServerHandler> _Nullable handler, int32_t maxLines); + +FOUNDATION_EXPORT id<LibboxHTTPClient> _Nullable LibboxNewHTTPClient(void); + +FOUNDATION_EXPORT LibboxPProfServer* _Nullable LibboxNewPProfServer(long port); + +FOUNDATION_EXPORT LibboxBoxService* _Nullable LibboxNewService(NSString* _Nullable configContent, id<LibboxPlatformInterface> _Nullable platformInterface, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT LibboxCommandClient* _Nullable LibboxNewStandaloneCommandClient(void); + +FOUNDATION_EXPORT LibboxWIFIState* _Nullable LibboxNewWIFIState(NSString* _Nullable wifiSSID, NSString* _Nullable wifiBSSID); + +FOUNDATION_EXPORT LibboxImportRemoteProfile* _Nullable LibboxParseRemoteProfileImportLink(NSString* _Nullable importLink, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxProxyDisplayType(NSString* _Nullable proxyType); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxReadServiceError(NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT BOOL LibboxRedirectStderr(NSString* _Nullable path, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT void LibboxRegisterLocalDNSTransport(id<LibboxLocalDNSTransport> _Nullable transport); + +FOUNDATION_EXPORT void LibboxSetMemoryLimit(BOOL enabled); + +FOUNDATION_EXPORT void LibboxSetup(NSString* _Nullable basePath, NSString* _Nullable workingPath, NSString* _Nullable tempPath, BOOL isTVOS); + +FOUNDATION_EXPORT BOOL LibboxSetupWithUsername(NSString* _Nullable basePath, NSString* _Nullable workingPath, NSString* _Nullable tempPath, NSString* _Nullable username, NSError* _Nullable* _Nullable error); + +FOUNDATION_EXPORT NSString* _Nonnull LibboxVersion(void); + +FOUNDATION_EXPORT BOOL LibboxWriteServiceError(NSString* _Nullable message, NSError* _Nullable* _Nullable error); + +@class LibboxCommandClientHandler; + +@class LibboxCommandServerHandler; + +@class LibboxFunc; + +@class LibboxHTTPClient; + +@class LibboxHTTPRequest; + +@class LibboxHTTPResponse; + +@class LibboxInterfaceUpdateListener; + +@class LibboxLocalDNSTransport; + +@class LibboxNetworkInterfaceIterator; + +@class LibboxOnDemandRule; + +@class LibboxOnDemandRuleIterator; + +@class LibboxOutboundGroupItemIterator; + +@class LibboxOutboundGroupIterator; + +@class LibboxPlatformInterface; + +@class LibboxProfilePreviewIterator; + +@class LibboxRoutePrefixIterator; + +@class LibboxStringIterator; + +@class LibboxTunInterface; + +@class LibboxTunOptions; + +@interface LibboxCommandClientHandler : NSObject <goSeqRefInterface, LibboxCommandClientHandler> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (void)clearLog; +- (void)connected; +- (void)disconnected:(NSString* _Nullable)message; +- (void)initializeClashMode:(id<LibboxStringIterator> _Nullable)modeList currentMode:(NSString* _Nullable)currentMode; +- (void)updateClashMode:(NSString* _Nullable)newMode; +- (void)writeGroups:(id<LibboxOutboundGroupIterator> _Nullable)message; +- (void)writeLog:(NSString* _Nullable)message; +- (void)writeStatus:(LibboxStatusMessage* _Nullable)message; +@end + +@interface LibboxCommandServerHandler : NSObject <goSeqRefInterface, LibboxCommandServerHandler> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (LibboxSystemProxyStatus* _Nullable)getSystemProxyStatus; +- (void)postServiceClose; +- (BOOL)serviceReload:(NSError* _Nullable* _Nullable)error; +- (BOOL)setSystemProxyEnabled:(BOOL)isEnabled error:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxFunc : NSObject <goSeqRefInterface, LibboxFunc> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)invoke:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxHTTPClient : NSObject <goSeqRefInterface, LibboxHTTPClient> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (void)close; +- (void)keepAlive; +- (void)modernTLS; +- (id<LibboxHTTPRequest> _Nullable)newRequest; +- (void)pinnedSHA256:(NSString* _Nullable)sumHex; +- (void)pinnedTLS12; +- (void)restrictedTLS; +- (void)trySocks5:(int32_t)port; +@end + +@interface LibboxHTTPRequest : NSObject <goSeqRefInterface, LibboxHTTPRequest> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (id<LibboxHTTPResponse> _Nullable)execute:(NSError* _Nullable* _Nullable)error; +- (void)randomUserAgent; +- (void)setContent:(NSData* _Nullable)content; +- (void)setContentString:(NSString* _Nullable)content; +- (void)setHeader:(NSString* _Nullable)key value:(NSString* _Nullable)value; +- (void)setMethod:(NSString* _Nullable)method; +- (BOOL)setURL:(NSString* _Nullable)link error:(NSError* _Nullable* _Nullable)error; +- (void)setUserAgent:(NSString* _Nullable)userAgent; +@end + +@interface LibboxHTTPResponse : NSObject <goSeqRefInterface, LibboxHTTPResponse> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (NSData* _Nullable)getContent:(NSError* _Nullable* _Nullable)error; +- (NSString* _Nonnull)getContentString:(NSError* _Nullable* _Nullable)error; +- (BOOL)writeTo:(NSString* _Nullable)path error:(NSError* _Nullable* _Nullable)error; +@end + +@interface LibboxInterfaceUpdateListener : NSObject <goSeqRefInterface, LibboxInterfaceUpdateListener> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (void)updateDefaultInterface:(NSString* _Nullable)interfaceName interfaceIndex:(int32_t)interfaceIndex; +@end + +@interface LibboxLocalDNSTransport : NSObject <goSeqRefInterface, LibboxLocalDNSTransport> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)exchange:(LibboxExchangeContext* _Nullable)ctx message:(NSData* _Nullable)message error:(NSError* _Nullable* _Nullable)error; +- (BOOL)lookup:(LibboxExchangeContext* _Nullable)ctx network:(NSString* _Nullable)network domain:(NSString* _Nullable)domain error:(NSError* _Nullable* _Nullable)error; +- (BOOL)raw; +@end + +@interface LibboxNetworkInterfaceIterator : NSObject <goSeqRefInterface, LibboxNetworkInterfaceIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxNetworkInterface* _Nullable)next; +@end + +@interface LibboxOnDemandRule : NSObject <goSeqRefInterface, LibboxOnDemandRule> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (id<LibboxStringIterator> _Nullable)dnsSearchDomainMatch; +- (id<LibboxStringIterator> _Nullable)dnsServerAddressMatch; +- (int32_t)interfaceTypeMatch; +- (NSString* _Nonnull)probeURL; +- (id<LibboxStringIterator> _Nullable)ssidMatch; +- (int32_t)target; +@end + +@interface LibboxOnDemandRuleIterator : NSObject <goSeqRefInterface, LibboxOnDemandRuleIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (id<LibboxOnDemandRule> _Nullable)next; +@end + +@interface LibboxOutboundGroupItemIterator : NSObject <goSeqRefInterface, LibboxOutboundGroupItemIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxOutboundGroupItem* _Nullable)next; +@end + +@interface LibboxOutboundGroupIterator : NSObject <goSeqRefInterface, LibboxOutboundGroupIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxOutboundGroup* _Nullable)next; +@end + +@interface LibboxPlatformInterface : NSObject <goSeqRefInterface, LibboxPlatformInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)autoDetectInterfaceControl:(int32_t)fd error:(NSError* _Nullable* _Nullable)error; +- (void)clearDNSCache; +- (BOOL)closeDefaultInterfaceMonitor:(id<LibboxInterfaceUpdateListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; +- (BOOL)findConnectionOwner:(int32_t)ipProtocol sourceAddress:(NSString* _Nullable)sourceAddress sourcePort:(int32_t)sourcePort destinationAddress:(NSString* _Nullable)destinationAddress destinationPort:(int32_t)destinationPort ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (id<LibboxNetworkInterfaceIterator> _Nullable)getInterfaces:(NSError* _Nullable* _Nullable)error; +- (BOOL)includeAllNetworks; +- (BOOL)openTun:(id<LibboxTunOptions> _Nullable)options ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (NSString* _Nonnull)packageNameByUid:(int32_t)uid error:(NSError* _Nullable* _Nullable)error; +- (LibboxWIFIState* _Nullable)readWIFIState; +- (BOOL)startDefaultInterfaceMonitor:(id<LibboxInterfaceUpdateListener> _Nullable)listener error:(NSError* _Nullable* _Nullable)error; +- (BOOL)uidByPackageName:(NSString* _Nullable)packageName ret0_:(int32_t* _Nullable)ret0_ error:(NSError* _Nullable* _Nullable)error; +- (BOOL)underNetworkExtension; +- (BOOL)usePlatformAutoDetectInterfaceControl; +- (BOOL)usePlatformDefaultInterfaceMonitor; +- (BOOL)usePlatformInterfaceGetter; +- (BOOL)useProcFS; +- (void)writeLog:(NSString* _Nullable)message; +@end + +@interface LibboxProfilePreviewIterator : NSObject <goSeqRefInterface, LibboxProfilePreviewIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxProfilePreview* _Nullable)next; +@end + +@interface LibboxRoutePrefixIterator : NSObject <goSeqRefInterface, LibboxRoutePrefixIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (LibboxRoutePrefix* _Nullable)next; +@end + +@interface LibboxStringIterator : NSObject <goSeqRefInterface, LibboxStringIterator> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)hasNext; +- (NSString* _Nonnull)next; +@end + +@interface LibboxTunInterface : NSObject <goSeqRefInterface, LibboxTunInterface> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)close:(NSError* _Nullable* _Nullable)error; +- (int32_t)fileDescriptor; +@end + +@interface LibboxTunOptions : NSObject <goSeqRefInterface, LibboxTunOptions> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (BOOL)getAutoRoute; +- (NSString* _Nonnull)getDNSServerAddress:(NSError* _Nullable* _Nullable)error; +- (id<LibboxStringIterator> _Nullable)getExcludePackage; +- (id<LibboxStringIterator> _Nullable)getHTTPProxyBypassDomain; +- (id<LibboxStringIterator> _Nullable)getHTTPProxyMatchDomain; +- (NSString* _Nonnull)getHTTPProxyServer; +- (int32_t)getHTTPProxyServerPort; +- (id<LibboxStringIterator> _Nullable)getIncludePackage; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4Address; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteExcludeAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet4RouteRange; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6Address; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteExcludeAddress; +- (id<LibboxRoutePrefixIterator> _Nullable)getInet6RouteRange; +- (int32_t)getMTU; +- (BOOL)getStrictRoute; +- (BOOL)isHTTPProxyEnabled; +@end + +#endif diff --git a/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/Universe.objc.h b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/Universe.objc.h new file mode 100644 index 0000000000000000000000000000000000000000..019e7502d581983722a15bf30799e85cbc5dd766 --- /dev/null +++ b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/Universe.objc.h @@ -0,0 +1,29 @@ +// Objective-C API for talking to Go package. +// gobind -lang=objc +// +// File is generated by gobind. Do not edit. + +#ifndef __Universe_H__ +#define __Universe_H__ + +@import Foundation; +#include "ref.h" + +@protocol Universeerror; +@class Universeerror; + +@protocol Universeerror <NSObject> +- (NSString* _Nonnull)error; +@end + +@class Universeerror; + +@interface Universeerror : NSError <goSeqRefInterface, Universeerror> { +} +@property(strong, readonly) _Nonnull id _ref; + +- (nonnull instancetype)initWithRef:(_Nonnull id)ref; +- (NSString* _Nonnull)error; +@end + +#endif diff --git a/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/ref.h b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/ref.h new file mode 100644 index 0000000000000000000000000000000000000000..b8036a4d85c7387f3def61473a071b5d8c4c8208 --- /dev/null +++ b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/ref.h @@ -0,0 +1,35 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifndef __GO_REF_HDR__ +#define __GO_REF_HDR__ + +#include <Foundation/Foundation.h> + +// GoSeqRef is an object tagged with an integer for passing back and +// forth across the language boundary. A GoSeqRef may represent either +// an instance of a Go object, or an Objective-C object passed to Go. +// The explicit allocation of a GoSeqRef is used to pin a Go object +// when it is passed to Objective-C. The Go seq package maintains a +// reference to the Go object in a map keyed by the refnum along with +// a reference count. When the reference count reaches zero, the Go +// seq package will clear the corresponding entry in the map. +@interface GoSeqRef : NSObject { +} +@property(readonly) int32_t refnum; +@property(strong) id obj; // NULL when representing a Go object. + +// new GoSeqRef object to proxy a Go object. The refnum must be +// provided from Go side. +- (instancetype)initWithRefnum:(int32_t)refnum obj:(id)obj; + +- (int32_t)incNum; + +@end + +@protocol goSeqRefInterface +-(GoSeqRef*) _ref; +@end + +#endif diff --git a/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Libbox b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Libbox new file mode 100644 index 0000000000000000000000000000000000000000..6c6b51842e6e7e97456b932bfb8728787d28b9d9 Binary files /dev/null and b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Libbox differ diff --git a/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Modules/module.modulemap b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Modules/module.modulemap new file mode 100644 index 0000000000000000000000000000000000000000..1ff5e29bdfd41d06429a16857384ad8cbffb85fe --- /dev/null +++ b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Modules/module.modulemap @@ -0,0 +1,8 @@ +framework module "Libbox" { + header "ref.h" + header "Libbox.objc.h" + header "Universe.objc.h" + header "Libbox.h" + + export * +} \ No newline at end of file diff --git a/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Resources/Info.plist b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Resources/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..0d1a4b8ab9b1fc8e9357197398f73353470cb636 --- /dev/null +++ b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/A/Resources/Info.plist @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> + <plist version="1.0"> + <dict> + </dict> + </plist> diff --git a/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/Current b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/Current new file mode 120000 index 0000000000000000000000000000000000000000..8c7e5a667f1b771847fe88c01c3de34413a1b220 --- /dev/null +++ b/Libbox.xcframework/tvos-arm64_x86_64-simulator/Libbox.framework/Versions/Current @@ -0,0 +1 @@ +A \ No newline at end of file diff --git a/Library/Database/Databse.swift b/Library/Database/Databse.swift new file mode 100644 index 0000000000000000000000000000000000000000..fea031bbecd5c09fa4f695011e46b3b163072482 --- /dev/null +++ b/Library/Database/Databse.swift @@ -0,0 +1,40 @@ +import Foundation +import GRDB + +actor Database { + private static var writer: (any DatabaseWriter)? + + static func sharedWriter() throws -> any DatabaseWriter { + if let writer { + return writer + } + try FileManager.default.createDirectory(at: FilePath.sharedDirectory, withIntermediateDirectories: true) + let database = try DatabasePool(path: FilePath.sharedDirectory.appendingPathComponent("settings.db").relativePath) + var migrator = DatabaseMigrator().disablingDeferredForeignKeyChecks() + migrator.registerMigration("initialize") { db in + try db.create(table: "profiles") { t in + t.autoIncrementedPrimaryKey("id") + t.column("name", .text).notNull() + t.column("order", .integer).notNull() + t.column("type", .integer).notNull().defaults(to: ProfileType.local.rawValue) + t.column("path", .text).notNull() + t.column("remoteURL", .text) + t.column("autoUpdate", .boolean).notNull().defaults(to: false) + t.column("lastUpdated", .datetime) + } + try db.create(table: "preferences") { t in + t.primaryKey("name", .text, onConflict: .replace).notNull() + t.column("data", .blob) + } + } + migrator.registerMigration("add_auto_update_interval") { db in + try db.alter(table: "profiles") { t in + t.add(column: "autoUpdateInterval", .integer).notNull().defaults(to: 0) + } + } + + try migrator.migrate(database) + writer = database + return database + } +} diff --git a/Library/Database/Profile+Date.swift b/Library/Database/Profile+Date.swift new file mode 100644 index 0000000000000000000000000000000000000000..c6f4b7177cf004197cc033e7bed69dc15ce82566 --- /dev/null +++ b/Library/Database/Profile+Date.swift @@ -0,0 +1,9 @@ +import Foundation + +public extension Profile { + var lastUpdatedString: String { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" + return dateFormatter.string(from: lastUpdated!) + } +} diff --git a/Library/Database/Profile+Hashable.swift b/Library/Database/Profile+Hashable.swift new file mode 100644 index 0000000000000000000000000000000000000000..a250097784b7649aa92c9ba386b361659ba30255 --- /dev/null +++ b/Library/Database/Profile+Hashable.swift @@ -0,0 +1,11 @@ +import Foundation + +extension Profile: Hashable { + public static func == (lhs: Profile, rhs: Profile) -> Bool { + lhs.id == rhs.id + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(id) + } +} diff --git a/Library/Database/Profile+RW.swift b/Library/Database/Profile+RW.swift new file mode 100644 index 0000000000000000000000000000000000000000..7899f50c5049d513a5dd64c2c82cec2120d35c7b --- /dev/null +++ b/Library/Database/Profile+RW.swift @@ -0,0 +1,31 @@ +import Foundation + +public extension Profile { + func read() throws -> String { + switch type { + case .local, .remote: + return try String(contentsOfFile: path) + case .icloud: + let saveURL = FilePath.iCloudDirectory.appendingPathComponent(path) + _ = saveURL.startAccessingSecurityScopedResource() + defer { + saveURL.stopAccessingSecurityScopedResource() + } + return try String(contentsOf: saveURL) + } + } + + func write(_ content: String) throws { + switch type { + case .local, .remote: + try content.write(toFile: path, atomically: true, encoding: .utf8) + case .icloud: + let saveURL = FilePath.iCloudDirectory.appendingPathComponent(path) + _ = saveURL.startAccessingSecurityScopedResource() + defer { + saveURL.stopAccessingSecurityScopedResource() + } + try content.write(to: saveURL, atomically: true, encoding: .utf8) + } + } +} diff --git a/Library/Database/Profile+Share.swift b/Library/Database/Profile+Share.swift new file mode 100644 index 0000000000000000000000000000000000000000..e276b93dc9cd9eaae2ee5a95d1202d5e0c0679d0 --- /dev/null +++ b/Library/Database/Profile+Share.swift @@ -0,0 +1,8 @@ +import Foundation +import Libbox + +public extension Profile { + var shareLink: URL { + URL(string: LibboxGenerateRemoteProfileImportLink(name, remoteURL!))! + } +} diff --git a/Library/Database/Profile+Transferable.swift b/Library/Database/Profile+Transferable.swift new file mode 100644 index 0000000000000000000000000000000000000000..b30142666c7d9ef9dd21a97359b20296b0dc079b --- /dev/null +++ b/Library/Database/Profile+Transferable.swift @@ -0,0 +1,106 @@ +import Foundation +import Libbox +import SwiftUI +import UniformTypeIdentifiers + +public extension Profile { + func toContent() throws -> LibboxProfileContent { + let content = LibboxProfileContent() + content.name = name + content.type = Int32(type.rawValue) + content.config = try read() + if type != .local { + content.remotePath = remoteURL! + } + if type == .remote { + content.autoUpdate = autoUpdate + content.autoUpdateInterval = autoUpdateInterval + if let lastUpdated { + content.lastUpdated = Int64(lastUpdated.timeIntervalSince1970) + } + } + return content + } +} + +@available(iOS 16.0, macOS 13.0, *) +extension Profile: Transferable { + public static var transferRepresentation: some TransferRepresentation { + ProxyRepresentation { profile in + try TypedProfile(profile.toContent()) + } + } +} + +public extension LibboxProfileContent { + static func from(_ data: Data) throws -> LibboxProfileContent { + var error: NSError? + let content = LibboxDecodeProfileContent(data, &error) + if let error { + throw error + } + return content! + } + + func importProfile() async throws { + let nextProfileID = try await ProfileManager.nextID() + let profileConfigDirectory = FilePath.sharedDirectory.appendingPathComponent("configs", isDirectory: true) + try FileManager.default.createDirectory(at: profileConfigDirectory, withIntermediateDirectories: true) + let profileConfig = profileConfigDirectory.appendingPathComponent("config_\(nextProfileID).json") + try config.write(to: profileConfig, atomically: true, encoding: .utf8) + var lastUpdatedAt: Date? + if lastUpdated > 0 { + lastUpdatedAt = Date(timeIntervalSince1970: Double(lastUpdated)) + } + try await ProfileManager.create(Profile(name: name, type: ProfileType(rawValue: Int(type))!, path: profileConfig.relativePath, remoteURL: remotePath, autoUpdate: autoUpdate, autoUpdateInterval: autoUpdateInterval, lastUpdated: lastUpdatedAt)) + } + + func generateShareFile() throws -> URL { + let shareDirectory = FilePath.cacheDirectory.appendingPathComponent("share", isDirectory: true) + try FileManager.default.createDirectory(at: shareDirectory, withIntermediateDirectories: true) + let shareFile = shareDirectory.appendingPathComponent("\(name).bpf") + try encode()!.write(to: shareFile) + return shareFile + } +} + +public extension String { + func generateShareFile(name: String) throws -> URL { + let shareDirectory = FilePath.cacheDirectory.appendingPathComponent("share", isDirectory: true) + try FileManager.default.createDirectory(at: shareDirectory, withIntermediateDirectories: true) + let shareFile = shareDirectory.appendingPathComponent(name) + try write(to: shareFile, atomically: true, encoding: .utf8) + return shareFile + } +} + +@available(iOS 16.0, macOS 13.0, *) +public struct TypedProfile: Transferable, Codable { + public let content: LibboxProfileContent + public init(_ content: LibboxProfileContent) { + self.content = content + } + + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let data = try container.decode(Data.self) + try self.init(.from(data)) + } + + public static var transferRepresentation: some TransferRepresentation { + FileRepresentation(contentType: .profile) { typed in + try SentTransferredFile(typed.content.generateShareFile(), allowAccessingOriginalFile: true) + } importing: { received in + try TypedProfile(.from(Data(contentsOf: received.file))) + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(content.encode()!) + } +} + +public extension UTType { + static var profile: UTType { .init(exportedAs: "io.nekohasekai.sfabeino.profile") } +} diff --git a/Library/Database/Profile+Update.swift b/Library/Database/Profile+Update.swift new file mode 100644 index 0000000000000000000000000000000000000000..c3bed660fe370c85857399594fc8e62254bc1161 --- /dev/null +++ b/Library/Database/Profile+Update.swift @@ -0,0 +1,20 @@ +import Foundation +import GRDB +import Libbox + +public extension Profile { + nonisolated func updateRemoteProfile() async throws { + if type != .remote { + return + } + let remoteContent = try HTTPClient().getString(remoteURL) + var error: NSError? + LibboxCheckConfig(remoteContent, &error) + if let error { + throw error + } + try write(remoteContent) + lastUpdated = Date() + try await ProfileManager.update(self) + } +} diff --git a/Library/Database/Profile.swift b/Library/Database/Profile.swift new file mode 100644 index 0000000000000000000000000000000000000000..0b49bf9e8b3757c8aaa91706fa63bf124fdcfe22 --- /dev/null +++ b/Library/Database/Profile.swift @@ -0,0 +1,100 @@ +import Foundation +import GRDB +import Network + +public class Profile: Record, Identifiable, ObservableObject { + public var id: Int64? + public var mustID: Int64 { + id! + } + + @Published public var name: String + public var order: UInt32 + public var type: ProfileType + public var path: String + @Published public var remoteURL: String? + @Published public var autoUpdate: Bool + @Published public var autoUpdateInterval: Int32 + public var lastUpdated: Date? + + public init(id: Int64? = nil, name: String, order: UInt32 = 0, type: ProfileType, path: String, remoteURL: String? = nil, autoUpdate: Bool = false, autoUpdateInterval: Int32 = 0, lastUpdated: Date? = nil) { + self.id = id + self.name = name + self.order = order + self.type = type + self.path = path + self.remoteURL = remoteURL + self.autoUpdate = autoUpdate + self.autoUpdateInterval = autoUpdateInterval + self.lastUpdated = lastUpdated + super.init() + } + + override public class var databaseTableName: String { + "profiles" + } + + enum Columns: String, ColumnExpression { + case id, name, order, type, path, remoteURL, autoUpdate, autoUpdateInterval, lastUpdated, userAgent + } + + required init(row: Row) throws { + id = row[Columns.id] + name = row[Columns.name] ?? "" + order = row[Columns.order] ?? 0 + type = ProfileType(rawValue: row[Columns.type] ?? ProfileType.local.rawValue)! + path = row[Columns.path] ?? "" + remoteURL = row[Columns.remoteURL] ?? "" + autoUpdate = row[Columns.autoUpdate] ?? false + autoUpdateInterval = row[Columns.autoUpdateInterval] ?? 0 + lastUpdated = row[Columns.lastUpdated] ?? Date() + try super.init(row: row) + } + + override public func encode(to container: inout PersistenceContainer) throws { + container[Columns.id] = id + container[Columns.name] = name + container[Columns.order] = order + container[Columns.type] = type.rawValue + container[Columns.path] = path + container[Columns.remoteURL] = remoteURL + container[Columns.autoUpdate] = autoUpdate + container[Columns.autoUpdateInterval] = autoUpdateInterval + container[Columns.lastUpdated] = lastUpdated + } + + override public func didInsert(_ inserted: InsertionSuccess) { + super.didInsert(inserted) + id = inserted.rowID + } +} + +public struct ProfilePreview: Identifiable, Hashable { + public let id: Int64 + public let name: String + public var order: UInt32 + public let type: ProfileType + public let path: String + public let remoteURL: String? + public let autoUpdate: Bool + public let autoUpdateInterval: Int32 + public let lastUpdated: Date? + public let origin: Profile + + public init(_ profile: Profile) { + id = profile.mustID + name = profile.name + order = profile.order + type = profile.type + path = profile.path + remoteURL = profile.remoteURL + autoUpdate = profile.autoUpdate + autoUpdateInterval = profile.autoUpdateInterval + lastUpdated = profile.lastUpdated + origin = profile + } +} + +public enum ProfileType: Int { + case local = 0, icloud, remote +} diff --git a/Library/Database/ProfileManager.swift b/Library/Database/ProfileManager.swift new file mode 100644 index 0000000000000000000000000000000000000000..c004d62b584d9b96c15fb0ee19236e451f9d8c1c --- /dev/null +++ b/Library/Database/ProfileManager.swift @@ -0,0 +1,98 @@ +import Foundation +import GRDB + +public enum ProfileManager { + public nonisolated static func create(_ profile: Profile) async throws { + profile.order = try await nextOrder() + try await Database.sharedWriter().write { db in + try profile.insert(db, onConflict: .fail) + } + } + + public nonisolated static func get(_ profileID: Int64) async throws -> Profile? { + try await Database.sharedWriter().read { db in + try Profile.fetchOne(db, id: profileID) + } + } + + public nonisolated static func get(by profileName: String) async throws -> Profile? { + try await Database.sharedWriter().read { db in + try Profile.filter(Column("name") == profileName).fetchOne(db) + } + } + + public nonisolated static func delete(_ profile: Profile) async throws { + _ = try await Database.sharedWriter().write { db in + try profile.delete(db) + } + } + + public nonisolated static func delete(by id: Int64) async throws { + _ = try await Database.sharedWriter().write { db in + try Profile.deleteOne(db, id: id) + } + } + + public nonisolated static func delete(_ profileList: [Profile]) async throws -> Int { + try await Database.sharedWriter().write { db in + try Profile.deleteAll(db, keys: profileList.map { + ["id": $0.id!] + }) + } + } + + public nonisolated static func delete(by id: [Int64]) async throws -> Int { + try await Database.sharedWriter().write { db in + try Profile.deleteAll(db, ids: id) + } + } + + public nonisolated static func update(_ profile: Profile) async throws { + _ = try await Database.sharedWriter().write { db in + try profile.updateChanges(db) + } + } + + public nonisolated static func update(_ profileList: [Profile]) async throws { + // TODO: batch update + try await Database.sharedWriter().write { db in + for profile in profileList { + try profile.updateChanges(db) + } + } + } + + public nonisolated static func list() async throws -> [Profile] { + try await Database.sharedWriter().read { db in + try Profile.all().order(Column("order").asc).fetchAll(db) + } + } + + public nonisolated static func listRemote() async throws -> [Profile] { + try await Database.sharedWriter().read { db in + try Profile.filter(Column("type") == ProfileType.remote.rawValue).order(Column("order").asc).fetchAll(db) + } + } + + public nonisolated static func listAutoUpdateEnabled() async throws -> [Profile] { + try await Database.sharedWriter().read { db in + try Profile.filter(Column("autoUpdate") == true).order(Column("order").asc).fetchAll(db) + } + } + + public nonisolated static func nextID() async throws -> Int64 { + try await Database.sharedWriter().read { db in + if let lastProfile = try Profile.select(Column("id")).order(Column("id").desc).fetchOne(db) { + return lastProfile.id! + 1 + } else { + return 1 + } + } + } + + private nonisolated static func nextOrder() async throws -> UInt32 { + try await Database.sharedWriter().read { db in + try UInt32(Profile.fetchCount(db)) + } + } +} diff --git a/Library/Database/ShadredPreferences+Database.swift b/Library/Database/ShadredPreferences+Database.swift new file mode 100644 index 0000000000000000000000000000000000000000..2cd9056092d9a0350a956b505b0677a160ce41a8 --- /dev/null +++ b/Library/Database/ShadredPreferences+Database.swift @@ -0,0 +1,95 @@ +import BinaryCodable +import Foundation +import GRDB + +extension SharedPreferences { + public class Preference<T: Codable> { + private let name: String + private let defaultValue: T + + init(_ name: String, defaultValue: T) { + self.name = name + self.defaultValue = defaultValue + } + + public nonisolated func get() async -> T { + do { + return try await SharedPreferences.read(name) ?? defaultValue + } catch { + NSLog("read preferences error: \(error)") + return defaultValue + } + } + + public func getBlocking() -> T { + runBlocking { [self] in + await get() + } + } + + public nonisolated func set(_ newValue: T?) async { + do { + try await SharedPreferences.write(name, newValue) + } catch { + NSLog("write preferences error: \(error)") + } + } + } + + private nonisolated static func read<T: Codable>(_ name: String) async throws -> T? { + guard let item = try await (Database.sharedWriter().read { db in + try Item.fetchOne(db, id: name) + }) + else { + return nil + } + return try BinaryDecoder().decode(from: item.data) + } + + private nonisolated static func write(_ name: String, _ value: (some Codable)?) async throws { + if value == nil { + _ = try await Database.sharedWriter().write { db in + try Item.deleteOne(db, id: name) + } + } else { + let data = try BinaryEncoder().encode(value) + try await Database.sharedWriter().write { db in + try Item(name: name, data: data).insert(db) + } + } + } +} + +private class Item: Record, Identifiable { + public var id: String { + name + } + + public var name: String + public var data: Data + + init(name: String, data: Data) { + self.name = name + self.data = data + super.init() + } + + override public class var databaseTableName: String { + "preferences" + } + + enum Columns: String, ColumnExpression { + case name, data + } + + required init(row: Row) throws { + name = row[Columns.name] + data = row[Columns.data] + try super.init(row: row) + } + + override public func encode(to container: inout PersistenceContainer) throws { + container[Columns.name] = name + container[Columns.data] = data + } +} diff --git a/Library/Database/SharedPreferences.swift b/Library/Database/SharedPreferences.swift new file mode 100644 index 0000000000000000000000000000000000000000..a0c2f5627f11ea97a10fb4974056a6f12a342d0a --- /dev/null +++ b/Library/Database/SharedPreferences.swift @@ -0,0 +1,84 @@ +import Foundation + +public enum SharedPreferences { + public static let selectedProfileID = Preference<Int64>("selected_profile_id", defaultValue: -1) + + #if os(macOS) + private static let ignoreMemoryLimitByDefault = true + #else + private static let ignoreMemoryLimitByDefault = false + #endif + + public static let ignoreMemoryLimit = Preference<Bool>("ignore_memory_limit", defaultValue: ignoreMemoryLimitByDefault) + + #if os(iOS) + public static let excludeLocalNetworksByDefault = true + #elseif os(macOS) + public static let excludeLocalNetworksByDefault = false + #endif + + #if !os(tvOS) + public static let includeAllNetworks = Preference<Bool>("include_all_networks", defaultValue: false) + public static let excludeAPNs = Preference<Bool>("exclude_apns", defaultValue: true) + public static let excludeLocalNetworks = Preference<Bool>("exclude_local_networks", defaultValue: excludeLocalNetworksByDefault) + public static let excludeCellularServices = Preference<Bool>("exclude_celluar_services", defaultValue: true) + public static let enforceRoutes = Preference<Bool>("enforce_routes", defaultValue: false) + + #endif + + public static func resetPacketTunnel() async { + await ignoreMemoryLimit.set(nil) + #if !os(tvOS) + await includeAllNetworks.set(nil) + await excludeAPNs.set(nil) + await excludeLocalNetworks.set(nil) + await excludeCellularServices.set(nil) + await enforceRoutes.set(nil) + #endif + } + + public static let maxLogLines = Preference<Int>("max_log_lines", defaultValue: 300) + + #if os(macOS) + public static let showMenuBarExtra = Preference<Bool>("show_menu_bar_extra", defaultValue: true) + public static let menuBarExtraInBackground = Preference<Bool>("menu_bar_extra_in_background", defaultValue: false) + public static let startedByUser = Preference<Bool>("started_by_user", defaultValue: false) + + public static func resetMacOS() async { + await showMenuBarExtra.set(nil) + await menuBarExtraInBackground.set(nil) + } + #endif + + #if os(iOS) + public static let networkPermissionRequested = Preference<Bool>("network_permission_requested", defaultValue: false) + #endif + + public static let systemProxyEnabled = Preference<Bool>("system_proxy_enabled", defaultValue: true) + + // Profile Override + + public static let excludeDefaultRoute = Preference<Bool>("exclude_default_route", defaultValue: false) + public static let autoRouteUseSubRangesByDefault = Preference<Bool>("auto_route_use_sub_ranges_by_default", defaultValue: false) + public static let excludeAPNsRoute = Preference<Bool>("exclude_apple_push_notification_services", defaultValue: false) + + public static func resetProfileOverride() async { + await excludeDefaultRoute.set(nil) + await autoRouteUseSubRangesByDefault.set(nil) + await excludeAPNsRoute.set(nil) + } + + // On Demand Rules + + public static let alwaysOn = Preference<Bool>("always_on", defaultValue: false) + + public static func resetOnDemandRules() async { + await alwaysOn.set(nil) + } + + #if DEBUG + public static let inDebug = true + #else + public static let inDebug = false + #endif +} diff --git a/Library/Discovery/NWSocket.swift b/Library/Discovery/NWSocket.swift new file mode 100644 index 0000000000000000000000000000000000000000..292c674d93d242f03e6fd21cb50c9ca85322d950 --- /dev/null +++ b/Library/Discovery/NWSocket.swift @@ -0,0 +1,63 @@ +import Foundation +import Libbox +import Network + +public class NWSocket { + private let connection: NWConnection + + public init(_ connection: NWConnection) { + self.connection = connection + } + + public func read() throws -> Data { + let semaphore = DispatchSemaphore(value: 0) + var result: Result<Data, Error>! + connection.receive(minimumIncompleteLength: 2, maximumLength: 2) { content, _, _, error in + if let error { + result = .failure(error) + } else { + result = .success(content!) + } + semaphore.signal() + } + semaphore.wait() + let lengthChunk = try result.get() + let length = Int(LibboxDecodeLengthChunk(lengthChunk)) + connection.receive(minimumIncompleteLength: length, maximumLength: length) { content, _, _, error in + if let error { + result = .failure(error) + } else { + result = .success(content!) + } + semaphore.signal() + } + semaphore.wait() + return try result.get() + } + + public func write(_ data: Data?) throws { + guard let data else { + return + } + let semaphore = DispatchSemaphore(value: 0) + var result: Error? + connection.send(content: LibboxEncodeChunkedMessage(data), isComplete: false, completion: .contentProcessed { error in + result = error + semaphore.wait() + }) + if let result { + throw result + } + } + + public func send(_ data: Data?) { + guard let data else { + return + } + connection.send(content: LibboxEncodeChunkedMessage(data), completion: .idempotent) + } + + public func cancel() { + connection.cancel() + } +} diff --git a/Library/Discovery/ProfileServer.swift b/Library/Discovery/ProfileServer.swift new file mode 100644 index 0000000000000000000000000000000000000000..596696e4d965a4ed3887288abcbe5f41bea9cd06 --- /dev/null +++ b/Library/Discovery/ProfileServer.swift @@ -0,0 +1,135 @@ +import Foundation +import Libbox +import Network + +public class ProfileServer { + private var listener: NWListener + + @available(iOS 16.0, macOS 13.0, *) + public init() throws { + listener = try NWListener(using: .applicationService) + listener.service = NWListener.Service(applicationService: "sing-box:profile") + listener.newConnectionHandler = { connection in + connection.stateUpdateHandler = { state in + if state == .ready { + Task.detached { + try await Task.sleep(nanoseconds: NSEC_PER_MSEC * 100) + await ProfileConnection(connection).process() + } + } + } + connection.start(queue: .global()) + } + } + + public func start() { + listener.start(queue: .global()) + } + + public func cancel() { + listener.cancel() + } + + class ProfileConnection { + private let connection: NWSocket + + init(_ connection: NWConnection) { + self.connection = NWSocket(connection) + } + + func process() async { + do { + try await writeProfilePreviewList() + } catch { + NSLog("profile server: write profile list: \(error.localizedDescription)") + writeError(error.localizedDescription) + return + } + do { + while true { + let message = try connection.read() + try processMessage(message) + } + } catch { + NSLog("profile server: process connection: \(error.localizedDescription)") + writeError(error.localizedDescription) + } + } + + private func processMessage(_ data: Data) throws { + if data.count == 0 { + return + } + let messageType = Int64(data[0]) + switch messageType { + case LibboxMessageTypeProfileContentRequest: + Task { + try await processProfileContentRequest(data) + } + default: + throw NSError(domain: "unexpected message type \(messageType)", code: 0) + } + } + + private func processProfileContentRequest(_ data: Data) async throws { + var error: NSError? + let request = LibboxDecodeProfileContentRequest(data, &error) + if let error { + throw error + } + + let profile = try await ProfileManager.get(request!.profileID) + guard let profile else { + throw NSError(domain: "profile not found", code: 0) + } + let content = LibboxProfileContent() + content.name = profile.name + switch profile.type { + case .local: + content.type = LibboxProfileTypeLocal + case .icloud: + content.type = LibboxProfileTypeiCloud + case .remote: + content.type = LibboxProfileTypeRemote + } + content.config = try profile.read() + if profile.type != .local { + content.remotePath = profile.remoteURL! + } + if profile.type == .remote { + content.autoUpdate = profile.autoUpdate + content.autoUpdateInterval = profile.autoUpdateInterval + if let lastUpdated = profile.lastUpdated { + content.lastUpdated = Int64(lastUpdated.timeIntervalSince1970) + } + } + try connection.write(content.encode()) + } + + private func writeProfilePreviewList() async throws { + let profiles = try await ProfileManager.list() + let encoder = LibboxProfileEncoder() + for profile in profiles { + let preview = LibboxProfilePreview() + preview.profileID = profile.mustID + preview.name = profile.name + switch profile.type { + case .local: + preview.type = LibboxProfileTypeLocal + case .icloud: + preview.type = LibboxProfileTypeiCloud + case .remote: + preview.type = LibboxProfileTypeRemote + } + encoder.append(preview) + } + try connection.write(encoder.encode()) + } + + private func writeError(_ message: String) { + let errorMessage = LibboxErrorMessage() + errorMessage.message = message + try? connection.write(errorMessage.encode()) + } + } +} diff --git a/Library/Library.swift b/Library/Library.swift new file mode 100644 index 0000000000000000000000000000000000000000..b0c1d88bb0ebd859d312b7a7ce2bcab2ccea0f41 --- /dev/null +++ b/Library/Library.swift @@ -0,0 +1,3 @@ +import Foundation + +public class Library {} diff --git a/Library/Network/CommandClient.swift b/Library/Network/CommandClient.swift new file mode 100644 index 0000000000000000000000000000000000000000..ab2f0bc101239aeea0a81664e4718d50a32d52d4 --- /dev/null +++ b/Library/Network/CommandClient.swift @@ -0,0 +1,165 @@ +import Foundation +import Libbox + +public class CommandClient: ObservableObject { + public enum ConnectionType { + case status + case groups + case log + case clashMode + } + + private let connectionType: ConnectionType + private let logMaxLines: Int + private var commandClient: LibboxCommandClient? + private var connectTask: Task<Void, Error>? + + @Published public var isConnected: Bool + @Published public var status: LibboxStatusMessage? + @Published public var groups: [LibboxOutboundGroup]? + @Published public var logList: [String] + @Published public var clashModeList: [String] + @Published public var clashMode: String + + public init(_ connectionType: ConnectionType, logMaxLines: Int = 300) { + self.connectionType = connectionType + self.logMaxLines = logMaxLines + logList = [] + clashModeList = [] + clashMode = "" + isConnected = false + } + + public func connect() { + if isConnected { + return + } + if let connectTask { + connectTask.cancel() + } + connectTask = Task { + await connect0() + } + } + + public func disconnect() { + if let connectTask { + connectTask.cancel() + self.connectTask = nil + } + if let commandClient { + try? commandClient.disconnect() + self.commandClient = nil + } + } + + private nonisolated func connect0() async { + let clientOptions = LibboxCommandClientOptions() + switch connectionType { + case .status: + clientOptions.command = LibboxCommandStatus + case .groups: + clientOptions.command = LibboxCommandGroup + case .log: + clientOptions.command = LibboxCommandLog + case .clashMode: + clientOptions.command = LibboxCommandClashMode + } + clientOptions.statusInterval = Int64(2 * NSEC_PER_SEC) + let client = LibboxNewCommandClient(clientHandler(self), clientOptions)! + do { + for i in 0 ..< 10 { + try await Task.sleep(nanoseconds: UInt64(Double(100 + (i * 50)) * Double(NSEC_PER_MSEC))) + try Task.checkCancellation() + do { + try client.connect() + await MainActor.run { + commandClient = client + } + return + } catch {} + try Task.checkCancellation() + } + } catch { + try? client.disconnect() + } + } + + private class clientHandler: NSObject, LibboxCommandClientHandlerProtocol { + private let commandClient: CommandClient + + init(_ commandClient: CommandClient) { + self.commandClient = commandClient + } + + func connected() { + DispatchQueue.main.async { [self] in + if commandClient.connectionType == .log { + commandClient.logList = [] + } + commandClient.isConnected = true + } + } + + func disconnected(_: String?) { + DispatchQueue.main.async { [self] in + commandClient.isConnected = false + } + } + + func clearLog() { + DispatchQueue.main.async { [self] in + commandClient.logList.removeAll() + } + } + + func writeLog(_ message: String?) { + guard let message else { + return + } + DispatchQueue.main.async { [self] in + if commandClient.logList.count > commandClient.logMaxLines { + commandClient.logList.removeFirst() + } + commandClient.logList.append(message) + } + } + + func writeStatus(_ message: LibboxStatusMessage?) { + DispatchQueue.main.async { [self] in + commandClient.status = message + } + } + + func writeGroups(_ groups: LibboxOutboundGroupIteratorProtocol?) { + guard let groups else { + return + } + var newGroups: [LibboxOutboundGroup] = [] + while groups.hasNext() { + newGroups.append(groups.next()!) + } + DispatchQueue.main.async { [self] in + commandClient.groups = newGroups + } + } + + func initializeClashMode(_ modeList: LibboxStringIteratorProtocol?, currentMode: String?) { + DispatchQueue.main.async { [self] in + commandClient.clashModeList = modeList!.toArray() + commandClient.clashMode = currentMode! + } + } + + func updateClashMode(_ newMode: String?) { + DispatchQueue.main.async { [self] in + commandClient.clashMode = newMode! + } + } + + // If LibboxConnections is required, make sure it is correctly imported and available + func write(_ message: Any?) { + // Handle the message appropriately + } + } +} diff --git a/Library/Network/Extension+Iterator.swift b/Library/Network/Extension+Iterator.swift new file mode 100644 index 0000000000000000000000000000000000000000..b7d57bb8f5fa887dfb86354bc29a1c0897823874 --- /dev/null +++ b/Library/Network/Extension+Iterator.swift @@ -0,0 +1,12 @@ +import Foundation +import Libbox + +extension LibboxStringIteratorProtocol { + func toArray() -> [String] { + var array: [String] = [] + while hasNext() { + array.append(next()) + } + return array + } +} diff --git a/Library/Network/Extension+RunBlocking.swift b/Library/Network/Extension+RunBlocking.swift new file mode 100644 index 0000000000000000000000000000000000000000..5a57bea9366f4dc2b3bb1eabf059a8d4f4f83599 --- /dev/null +++ b/Library/Network/Extension+RunBlocking.swift @@ -0,0 +1,36 @@ +import Foundation +import Libbox +import NetworkExtension + +func runBlocking<T>(_ block: @escaping () async -> T) -> T { + let semaphore = DispatchSemaphore(value: 0) + let box = resultBox<T>() + Task.detached { + let value = await block() + box.result0 = value + semaphore.signal() + } + semaphore.wait() + return box.result0 +} + +func runBlocking<T>(_ tBlock: @escaping () async throws -> T) throws -> T { + let semaphore = DispatchSemaphore(value: 0) + let box = resultBox<T>() + Task.detached { + do { + let value = try await tBlock() + box.result = .success(value) + } catch { + box.result = .failure(error) + } + semaphore.signal() + } + semaphore.wait() + return try box.result.get() +} + +private class resultBox<T> { + var result: Result<T, Error>! + var result0: T! +} diff --git a/Library/Network/ExtensionEnvironments.swift b/Library/Network/ExtensionEnvironments.swift new file mode 100644 index 0000000000000000000000000000000000000000..60601fcc77cc725c8cf99d868209dc08e20cba30 --- /dev/null +++ b/Library/Network/ExtensionEnvironments.swift @@ -0,0 +1,48 @@ +import Foundation +import SwiftUI + +public class ExtensionEnvironments: ObservableObject { + @Published public var logClient = CommandClient(.log) + @Published public var extensionProfileLoading = true + @Published public var extensionProfile: ExtensionProfile? + @Published public var emptyProfiles = false + + public let profileUpdate = ObjectWillChangePublisher() + public let selectedProfileUpdate = ObjectWillChangePublisher() + public let openSettings = ObjectWillChangePublisher() + + public init() {} + + deinit { + logClient.disconnect() + } + + public func postReload() { + Task { + await reload() + } + } + + @MainActor + public func reload() async { + if let newProfile = try? await ExtensionProfile.load() { + if extensionProfile == nil || extensionProfile?.status == .invalid { + newProfile.register() + extensionProfile = newProfile + extensionProfileLoading = false + } + } else { + extensionProfile = nil + extensionProfileLoading = false + } + } + + public func connectLog() { + guard let profile = extensionProfile else { + return + } + if profile.status.isConnected, !logClient.isConnected { + logClient.connect() + } + } +} diff --git a/Library/Network/ExtensionPlatformInterface.swift b/Library/Network/ExtensionPlatformInterface.swift new file mode 100644 index 0000000000000000000000000000000000000000..f29705eeb3da75572fcaf81113be2ccdce6a12ef --- /dev/null +++ b/Library/Network/ExtensionPlatformInterface.swift @@ -0,0 +1,338 @@ +import Foundation +import Libbox +import NetworkExtension +#if canImport(CoreWLAN) + import CoreWLAN +#endif + +public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtocol, LibboxCommandServerHandlerProtocol { + private let tunnel: ExtensionProvider + private var networkSettings: NEPacketTunnelNetworkSettings? + + init(_ tunnel: ExtensionProvider) { + self.tunnel = tunnel + } + + public func openTun(_ options: LibboxTunOptionsProtocol?, ret0_: UnsafeMutablePointer<Int32>?) throws { + try runBlocking { [self] in + try await openTun0(options, ret0_) + } + } + + private func openTun0(_ options: LibboxTunOptionsProtocol?, _ ret0_: UnsafeMutablePointer<Int32>?) async throws { + guard let options else { + throw NSError(domain: "nil options", code: 0) + } + guard let ret0_ else { + throw NSError(domain: "nil return pointer", code: 0) + } + + let autoRouteUseSubRangesByDefault = await SharedPreferences.autoRouteUseSubRangesByDefault.get() + let excludeAPNs = await SharedPreferences.excludeAPNsRoute.get() + + let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1") + if options.getAutoRoute() { + settings.mtu = NSNumber(value: options.getMTU()) + + var error: NSError? + let dnsServer = options.getDNSServerAddress(&error) + if let error { + throw error + } + settings.dnsSettings = NEDNSSettings(servers: [dnsServer]) + + var ipv4Address: [String] = [] + var ipv4Mask: [String] = [] + let ipv4AddressIterator = options.getInet4Address()! + while ipv4AddressIterator.hasNext() { + let ipv4Prefix = ipv4AddressIterator.next()! + ipv4Address.append(ipv4Prefix.address()) + ipv4Mask.append(ipv4Prefix.mask()) + } + + let ipv4Settings = NEIPv4Settings(addresses: ipv4Address, subnetMasks: ipv4Mask) + var ipv4Routes: [NEIPv4Route] = [] + var ipv4ExcludeRoutes: [NEIPv4Route] = [] + + let inet4RouteAddressIterator = options.getInet4RouteAddress()! + if inet4RouteAddressIterator.hasNext() { + while inet4RouteAddressIterator.hasNext() { + let ipv4RoutePrefix = inet4RouteAddressIterator.next()! + ipv4Routes.append(NEIPv4Route(destinationAddress: ipv4RoutePrefix.address(), subnetMask: ipv4RoutePrefix.mask())) + } + } else if autoRouteUseSubRangesByDefault { + ipv4Routes.append(NEIPv4Route(destinationAddress: "1.0.0.0", subnetMask: "255.0.0.0")) + ipv4Routes.append(NEIPv4Route(destinationAddress: "2.0.0.0", subnetMask: "254.0.0.0")) + ipv4Routes.append(NEIPv4Route(destinationAddress: "4.0.0.0", subnetMask: "252.0.0.0")) + ipv4Routes.append(NEIPv4Route(destinationAddress: "8.0.0.0", subnetMask: "248.0.0.0")) + ipv4Routes.append(NEIPv4Route(destinationAddress: "16.0.0.0", subnetMask: "240.0.0.0")) + ipv4Routes.append(NEIPv4Route(destinationAddress: "32.0.0.0", subnetMask: "224.0.0.0")) + ipv4Routes.append(NEIPv4Route(destinationAddress: "64.0.0.0", subnetMask: "192.0.0.0")) + ipv4Routes.append(NEIPv4Route(destinationAddress: "128.0.0.0", subnetMask: "128.0.0.0")) + } else { + ipv4Routes.append(NEIPv4Route.default()) + } + + let inet4RouteExcludeAddressIterator = options.getInet4RouteExcludeAddress()! + while inet4RouteExcludeAddressIterator.hasNext() { + let ipv4RoutePrefix = inet4RouteExcludeAddressIterator.next()! + ipv4ExcludeRoutes.append(NEIPv4Route(destinationAddress: ipv4RoutePrefix.address(), subnetMask: ipv4RoutePrefix.mask())) + } + if await SharedPreferences.excludeDefaultRoute.get(), !ipv4Routes.isEmpty { + if !ipv4ExcludeRoutes.contains(where: { it in + it.destinationAddress == "0.0.0.0" && it.destinationSubnetMask == "255.255.255.254" + }) { + ipv4ExcludeRoutes.append(NEIPv4Route(destinationAddress: "0.0.0.0", subnetMask: "255.255.255.254")) + } + } + if excludeAPNs, !ipv4Routes.isEmpty { + if !ipv4ExcludeRoutes.contains(where: { it in + it.destinationAddress == "17.0.0.0" && it.destinationSubnetMask == "255.0.0.0" + }) { + ipv4ExcludeRoutes.append(NEIPv4Route(destinationAddress: "17.0.0.0", subnetMask: "255.0.0.0")) + } + } + + ipv4Settings.includedRoutes = ipv4Routes + ipv4Settings.excludedRoutes = ipv4ExcludeRoutes + settings.ipv4Settings = ipv4Settings + + var ipv6Address: [String] = [] + var ipv6Prefixes: [NSNumber] = [] + let ipv6AddressIterator = options.getInet6Address()! + while ipv6AddressIterator.hasNext() { + let ipv6Prefix = ipv6AddressIterator.next()! + ipv6Address.append(ipv6Prefix.address()) + ipv6Prefixes.append(NSNumber(value: ipv6Prefix.prefix())) + } + let ipv6Settings = NEIPv6Settings(addresses: ipv6Address, networkPrefixLengths: ipv6Prefixes) + var ipv6Routes: [NEIPv6Route] = [] + var ipv6ExcludeRoutes: [NEIPv6Route] = [] + + let inet6RouteAddressIterator = options.getInet6RouteAddress()! + if inet6RouteAddressIterator.hasNext() { + while inet6RouteAddressIterator.hasNext() { + let ipv6RoutePrefix = inet6RouteAddressIterator.next()! + ipv6Routes.append(NEIPv6Route(destinationAddress: ipv6RoutePrefix.address(), networkPrefixLength: NSNumber(value: ipv6RoutePrefix.prefix()))) + } + } else if autoRouteUseSubRangesByDefault { + ipv6Routes.append(NEIPv6Route(destinationAddress: "100::", networkPrefixLength: 8)) + ipv6Routes.append(NEIPv6Route(destinationAddress: "200::", networkPrefixLength: 7)) + ipv6Routes.append(NEIPv6Route(destinationAddress: "400::", networkPrefixLength: 6)) + ipv6Routes.append(NEIPv6Route(destinationAddress: "800::", networkPrefixLength: 5)) + ipv6Routes.append(NEIPv6Route(destinationAddress: "1000::", networkPrefixLength: 4)) + ipv6Routes.append(NEIPv6Route(destinationAddress: "2000::", networkPrefixLength: 3)) + ipv6Routes.append(NEIPv6Route(destinationAddress: "4000::", networkPrefixLength: 2)) + ipv6Routes.append(NEIPv6Route(destinationAddress: "8000::", networkPrefixLength: 1)) + } else { + ipv6Routes.append(NEIPv6Route.default()) + } + + let inet6RouteExcludeAddressIterator = options.getInet6RouteExcludeAddress()! + while inet6RouteExcludeAddressIterator.hasNext() { + let ipv6RoutePrefix = inet6RouteExcludeAddressIterator.next()! + ipv6ExcludeRoutes.append(NEIPv6Route(destinationAddress: ipv6RoutePrefix.address(), networkPrefixLength: NSNumber(value: ipv6RoutePrefix.prefix()))) + } + + ipv6Settings.includedRoutes = ipv6Routes + ipv6Settings.excludedRoutes = ipv6ExcludeRoutes + settings.ipv6Settings = ipv6Settings + } + + if options.isHTTPProxyEnabled() { + let proxySettings = NEProxySettings() + let proxyServer = NEProxyServer(address: options.getHTTPProxyServer(), port: Int(options.getHTTPProxyServerPort())) + proxySettings.httpServer = proxyServer + proxySettings.httpsServer = proxyServer + if await SharedPreferences.systemProxyEnabled.get() { + proxySettings.httpEnabled = true + proxySettings.httpsEnabled = true + } + var bypassDomains: [String] = [] + let bypassDomainIterator = options.getHTTPProxyBypassDomain()! + while bypassDomainIterator.hasNext() { + bypassDomains.append(bypassDomainIterator.next()) + } + if excludeAPNs { + if !bypassDomains.contains(where: { it in + it == "push.apple.com" + }) { + bypassDomains.append("push.apple.com") + } + } + if !bypassDomains.isEmpty { + proxySettings.exceptionList = bypassDomains + } + var matchDomains: [String] = [] + let matchDomainIterator = options.getHTTPProxyMatchDomain()! + while matchDomainIterator.hasNext() { + matchDomains.append(matchDomainIterator.next()) + } + if !matchDomains.isEmpty { + proxySettings.matchDomains = matchDomains + } + settings.proxySettings = proxySettings + } + + networkSettings = settings + try await tunnel.setTunnelNetworkSettings(settings) + + if let tunFd = tunnel.packetFlow.value(forKeyPath: "socket.fileDescriptor") as? Int32 { + ret0_.pointee = tunFd + return + } + + let tunFdFromLoop = LibboxGetTunnelFileDescriptor() + if tunFdFromLoop != -1 { + ret0_.pointee = tunFdFromLoop + } else { + throw NSError(domain: "missing file descriptor", code: 0) + } + } + + public func usePlatformAutoDetectControl() -> Bool { + true + } + + public func autoDetectControl(_: Int32) throws {} + + public func findConnectionOwner(_: Int32, sourceAddress _: String?, sourcePort _: Int32, destinationAddress _: String?, destinationPort _: Int32, ret0_ _: UnsafeMutablePointer<Int32>?) throws { + throw NSError(domain: "not implemented", code: 0) + } + + public func packageName(byUid _: Int32, error _: NSErrorPointer) -> String { + "" + } + + public func uid(byPackageName _: String?, ret0_ _: UnsafeMutablePointer<Int32>?) throws { + throw NSError(domain: "not implemented", code: 0) + } + + public func useProcFS() -> Bool { + false + } + + public func writeLog(_ message: String?) { + guard let message else { + return + } + tunnel.writeMessage(message) + } + + public func usePlatformDefaultInterfaceMonitor() -> Bool { + false + } + + public func startDefaultInterfaceMonitor(_: LibboxInterfaceUpdateListenerProtocol?) throws {} + + public func closeDefaultInterfaceMonitor(_: LibboxInterfaceUpdateListenerProtocol?) throws {} + + public func useGetter() -> Bool { + false + } + + public func getInterfaces() throws -> LibboxNetworkInterfaceIteratorProtocol { + throw NSError(domain: "not implemented", code: 0) + } + + public func underNetworkExtension() -> Bool { + true + } + + public func includeAllNetworks() -> Bool { + #if !os(tvOS) + return SharedPreferences.includeAllNetworks.getBlocking() + #else + return false + #endif + } + + public func clearDNSCache() { + guard let networkSettings else { + return + } + tunnel.reasserting = true + tunnel.setTunnelNetworkSettings(nil) { _ in + } + tunnel.setTunnelNetworkSettings(networkSettings) { _ in + } + tunnel.reasserting = false + } + + public func readWIFIState() -> LibboxWIFIState? { + #if os(iOS) + let network = runBlocking { + await NEHotspotNetwork.fetchCurrent() + } + guard let network else { + return nil + } + return LibboxWIFIState(network.ssid, wifiBSSID: network.bssid)! + #elseif os(macOS) + guard let interface = CWWiFiClient.shared().interface() else { + return nil + } + guard let ssid = interface.ssid() else { + return nil + } + guard let bssid = interface.bssid() else { + return nil + } + return LibboxWIFIState(ssid, wifiBSSID: bssid)! + #else + return nil + #endif + } + + public func serviceReload() throws { + runBlocking { [self] in + await tunnel.reloadService() + } + } + + public func postServiceClose() { + reset() + tunnel.postServiceClose() + } + + public func getSystemProxyStatus() -> LibboxSystemProxyStatus? { + let status = LibboxSystemProxyStatus() + guard let networkSettings else { + return status + } + guard let proxySettings = networkSettings.proxySettings else { + return status + } + if proxySettings.httpServer == nil { + return status + } + status.available = true + status.enabled = proxySettings.httpEnabled + return status + } + + public func setSystemProxyEnabled(_ isEnabled: Bool) throws { + guard let networkSettings else { + return + } + guard let proxySettings = networkSettings.proxySettings else { + return + } + if proxySettings.httpServer == nil { + return + } + if proxySettings.httpEnabled == isEnabled { + return + } + proxySettings.httpEnabled = isEnabled + proxySettings.httpsEnabled = isEnabled + networkSettings.proxySettings = proxySettings + try runBlocking { + try await self.tunnel.setTunnelNetworkSettings(networkSettings) + } + } + + func reset() { + networkSettings = nil + } +} diff --git a/Library/Network/ExtensionProfile.swift b/Library/Network/ExtensionProfile.swift new file mode 100644 index 0000000000000000000000000000000000000000..c7381110f8e94b46504ffdcdadb79a3c49da19a6 --- /dev/null +++ b/Library/Network/ExtensionProfile.swift @@ -0,0 +1,124 @@ +import Foundation +import Libbox +import NetworkExtension + +public class ExtensionProfile: ObservableObject { + private let manager: NEVPNManager + private var connection: NEVPNConnection + private var observer: Any? + + @Published public var status: NEVPNStatus + + public init(_ manager: NEVPNManager) { + self.manager = manager + connection = manager.connection + status = manager.connection.status + } + + public func register() { + observer = NotificationCenter.default.addObserver( + forName: NSNotification.Name.NEVPNStatusDidChange, + object: manager.connection, + queue: .main + ) { [weak self] notification in + guard let self else { + return + } + self.connection = notification.object as! NEVPNConnection + self.status = self.connection.status + } + } + + private func unregister() { + if let observer { + NotificationCenter.default.removeObserver(observer) + } + } + + private func setOnDemandRules() { + let interfaceRule = NEOnDemandRuleConnect() + interfaceRule.interfaceTypeMatch = .any + let probeRule = NEOnDemandRuleConnect() + probeRule.probeURL = URL(string: "http://captive.apple.com") + manager.onDemandRules = [interfaceRule, probeRule] + } + + public func updateAlwaysOn(_ newState: Bool) async throws { + manager.isOnDemandEnabled = newState + setOnDemandRules() + try await manager.saveToPreferences() + } + + public func start() async throws { + await fetchProfile() + manager.isEnabled = true + if await SharedPreferences.alwaysOn.get() { + manager.isOnDemandEnabled = true + setOnDemandRules() + } + #if !os(tvOS) + if let protocolConfiguration = manager.protocolConfiguration { + let includeAllNetworks = await SharedPreferences.includeAllNetworks.get() + protocolConfiguration.includeAllNetworks = includeAllNetworks + if #available(iOS 16.4, macOS 13.3, *) { + protocolConfiguration.excludeCellularServices = !includeAllNetworks + } + } + #endif + try await manager.saveToPreferences() + #if os(macOS) + if Variant.useSystemExtension { + try manager.connection.startVPNTunnel(options: [ + "username": NSString(string: NSUserName()), + ]) + return + } + #endif + try manager.connection.startVPNTunnel() + } + + public func fetchProfile() async { + do { + if let profile = try await ProfileManager.get(Int64(SharedPreferences.selectedProfileID.get())) { + if profile.type == .icloud { + _ = try profile.read() + } + } + } catch {} + } + + public func stop() async throws { + if manager.isOnDemandEnabled { + manager.isOnDemandEnabled = false + try await manager.saveToPreferences() + } + do { + try LibboxNewStandaloneCommandClient()!.serviceClose() + } catch {} + manager.connection.stopVPNTunnel() + } + + public static func load() async throws -> ExtensionProfile? { + let managers = try await NETunnelProviderManager.loadAllFromPreferences() + if managers.isEmpty { + return nil + } + let profile = ExtensionProfile(managers[0]) + return profile + } + + public static func install() async throws { + let manager = NETunnelProviderManager() + manager.localizedDescription = Variant.applicationName + let tunnelProtocol = NETunnelProviderProtocol() + if Variant.useSystemExtension { + tunnelProtocol.providerBundleIdentifier = "\(FilePath.packageName).system" + } else { + tunnelProtocol.providerBundleIdentifier = "\(FilePath.packageName).extension" + } + tunnelProtocol.serverAddress = "sing-box" + manager.protocolConfiguration = tunnelProtocol + manager.isEnabled = true + try await manager.saveToPreferences() + } +} diff --git a/Library/Network/ExtensionProvider.swift b/Library/Network/ExtensionProvider.swift new file mode 100644 index 0000000000000000000000000000000000000000..54199ddb56d20cff0c231f1ea7ced7c30570c06a --- /dev/null +++ b/Library/Network/ExtensionProvider.swift @@ -0,0 +1,173 @@ +import Foundation +import Libbox +import NetworkExtension + +open class ExtensionProvider: NEPacketTunnelProvider { + public var username: String? = nil + private var commandServer: LibboxCommandServer! + private var boxService: LibboxBoxService! + private var systemProxyAvailable = false + private var systemProxyEnabled = false + private var platformInterface: ExtensionPlatformInterface! + + override open func startTunnel(options _: [String: NSObject]?) async throws { + LibboxClearServiceError() + + if let username { + var error: NSError? + LibboxSetupWithUsername(FilePath.sharedDirectory.relativePath, FilePath.workingDirectory.relativePath, FilePath.cacheDirectory.relativePath, username, &error) + if let error { + writeFatalError("(packet-tunnel) error: setup service: \(error.localizedDescription)") + return + } + } else { + var isTVOS = false + #if os(tvOS) + isTVOS = true + #endif + LibboxSetup(FilePath.sharedDirectory.relativePath, FilePath.workingDirectory.relativePath, FilePath.cacheDirectory.relativePath, isTVOS) + } + + var error: NSError? + LibboxRedirectStderr(FilePath.cacheDirectory.appendingPathComponent("stderr.log").relativePath, &error) + if let error { + writeFatalError("(packet-tunnel) redirect stderr error: \(error.localizedDescription)") + } + + await LibboxSetMemoryLimit(!SharedPreferences.ignoreMemoryLimit.get()) + + if platformInterface == nil { + platformInterface = ExtensionPlatformInterface(self) + } + commandServer = await LibboxNewCommandServer(platformInterface, Int32(SharedPreferences.maxLogLines.get())) + do { + try commandServer.start() + } catch { + writeFatalError("(packet-tunnel): log server start error: \(error.localizedDescription)") + return + } + writeMessage("(packet-tunnel): Here I stand") + await startService() + } + + func writeMessage(_ message: String) { + if let commandServer { + commandServer.writeMessage(message) + } else { + NSLog(message) + } + } + + public func writeFatalError(_ message: String) { + #if DEBUG + NSLog(message) + #endif + writeMessage(message) + var error: NSError? + LibboxWriteServiceError(message, &error) + cancelTunnelWithError(NSError(domain: message, code: 0)) + } + + private func startService() async { + let profile: Profile? + do { + profile = try await ProfileManager.get(Int64(SharedPreferences.selectedProfileID.get())) + } catch { + writeFatalError("(packet-tunnel) error: read selected profile: \(error.localizedDescription)") + return + } + guard let profile else { + writeFatalError("(packet-tunnel) error: missing selected profile") + return + } + let configContent: String + do { + configContent = try profile.read() + } catch { + writeFatalError("(packet-tunnel) error: read config file \(profile.path): \(error.localizedDescription)") + return + } + var error: NSError? + let service = LibboxNewService(configContent, platformInterface, &error) + if let error { + writeFatalError("(packet-tunnel) error: create service: \(error.localizedDescription)") + return + } + guard let service else { + return + } + commandServer.setService(service) + do { + try service.start() + } catch { + commandServer.setService(nil) + writeFatalError("(packet-tunnel) error: start service: \(error.localizedDescription)") + return + } + boxService = service + #if os(macOS) + await SharedPreferences.startedByUser.set(true) + #endif + } + + private func stopService() { + if let service = boxService { + do { + try service.close() + } catch { + writeMessage("(packet-tunnel) error: stop service: \(error.localizedDescription)") + } + boxService = nil + commandServer.setService(nil) + } + if let platformInterface { + platformInterface.reset() + } + } + + func reloadService() async { + writeMessage("(packet-tunnel) reloading service") + reasserting = true + defer { + reasserting = false + } + stopService() + commandServer.resetLog() + await startService() + } + + func postServiceClose() { + boxService = nil + } + + override open func stopTunnel(with reason: NEProviderStopReason) async { + writeMessage("(packet-tunnel) stopping, reason: \(reason)") + stopService() + if let server = commandServer { + try? await Task.sleep(nanoseconds: 100 * NSEC_PER_MSEC) + try? server.close() + commandServer = nil + } + #if os(macOS) + if reason == .userInitiated { + await SharedPreferences.startedByUser.set(reason == .userInitiated) + } + #endif + } + + override open func handleAppMessage(_ messageData: Data) async -> Data? { + messageData + } + + override open func sleep() async { + if let boxService { + boxService.pause() + } + } + + override open func wake() { + if let boxService { + boxService.wake() + } + } +} diff --git a/Library/Network/HTTPClient.swift b/Library/Network/HTTPClient.swift new file mode 100644 index 0000000000000000000000000000000000000000..5355b6ea4414eac5db78a1ddc444fcfe88ea2998 --- /dev/null +++ b/Library/Network/HTTPClient.swift @@ -0,0 +1,40 @@ +import Foundation +import Libbox + +public class HTTPClient { + private static var userAgent: String { + var userAgent = Variant.applicationName + userAgent += "/" + userAgent += Bundle.main.version + userAgent += " (Build " + userAgent += Bundle.main.versionNumber + userAgent += "; sing-box " + userAgent += LibboxVersion() + userAgent += ")" + return userAgent + } + + private let client: any LibboxHTTPClientProtocol + + public init() { + client = LibboxNewHTTPClient()! + client.modernTLS() + } + + public func getString(_ url: String?) throws -> String { + let request = client.newRequest()! + request.setUserAgent(HTTPClient.userAgent) + try request.setURL(url) + let response = try request.execute() + var error: NSError? + let contentString = response.getContentString(&error) + if let error { + throw error + } + return contentString + } + + deinit { + client.close() + } +} diff --git a/Library/Network/NEVPNStatus+isConnected.swift b/Library/Network/NEVPNStatus+isConnected.swift new file mode 100644 index 0000000000000000000000000000000000000000..d5b626bf8898c2372923da4062f9a55f20ad2b11 --- /dev/null +++ b/Library/Network/NEVPNStatus+isConnected.swift @@ -0,0 +1,40 @@ +import Foundation +import NetworkExtension + +public extension NEVPNStatus { + var isEnabled: Bool { + switch self { + case .connected, .disconnected, .reasserting: + return true + default: + return false + } + } + + var isSwitchable: Bool { + switch self { + case .connected, .disconnected: + return true + default: + return false + } + } + + var isConnected: Bool { + switch self { + case .connecting, .connected, .disconnecting, .reasserting: + return true + default: + return false + } + } + + var isConnectedStrict: Bool { + switch self { + case .connected, .reasserting: + return true + default: + return false + } + } +} diff --git a/Library/Network/SystemExtension.swift b/Library/Network/SystemExtension.swift new file mode 100644 index 0000000000000000000000000000000000000000..58c5eff704f80a53aab05f6a1db8d9e26d748719 --- /dev/null +++ b/Library/Network/SystemExtension.swift @@ -0,0 +1,126 @@ +#if os(macOS) + import Foundation + import SystemExtensions + + public class SystemExtension: NSObject, OSSystemExtensionRequestDelegate { + private let forceUpdate: Bool + private let inBackground: Bool + private let semaphore = DispatchSemaphore(value: 0) + private var result: OSSystemExtensionRequest.Result? + private var properties: [OSSystemExtensionProperties]? + private var error: Error? + + private init(_ forceUpdate: Bool = false, _ inBackground: Bool = false) { + self.forceUpdate = forceUpdate + self.inBackground = inBackground + } + + public func request(_: OSSystemExtensionRequest, actionForReplacingExtension existing: OSSystemExtensionProperties, withExtension ext: OSSystemExtensionProperties) -> OSSystemExtensionRequest.ReplacementAction { + if forceUpdate { + return .replace + } + if existing.isAwaitingUserApproval, !inBackground { + return .replace + } + if existing.bundleIdentifier == ext.bundleIdentifier, + existing.bundleVersion == ext.bundleVersion, + existing.bundleShortVersion == ext.bundleShortVersion + { + NSLog("Skip update system extension") + return .cancel + } else { + NSLog("Update system extension") + return .replace + } + } + + public func requestNeedsUserApproval(_: OSSystemExtensionRequest) { + semaphore.signal() + } + + public func request(_: OSSystemExtensionRequest, didFinishWithResult result: OSSystemExtensionRequest.Result) { + self.result = result + semaphore.signal() + } + + public func request(_: OSSystemExtensionRequest, didFailWithError error: Error) { + self.error = error + semaphore.signal() + } + + public func request(_: OSSystemExtensionRequest, foundProperties properties: [OSSystemExtensionProperties]) { + self.properties = properties + semaphore.signal() + } + + public func activation() throws -> OSSystemExtensionRequest.Result? { + let request = OSSystemExtensionRequest.activationRequest(forExtensionWithIdentifier: FilePath.packageName + ".system", queue: .main) + request.delegate = self + OSSystemExtensionManager.shared.submitRequest(request) + semaphore.wait() + if let error { + throw error + } + return result + } + + public func deactivation() throws -> OSSystemExtensionRequest.Result? { + let request = OSSystemExtensionRequest.deactivationRequest(forExtensionWithIdentifier: FilePath.packageName + ".system", queue: .main) + request.delegate = self + OSSystemExtensionManager.shared.submitRequest(request) + semaphore.wait() + if let error { + throw error + } + return result + } + + public func getProperties() throws -> [OSSystemExtensionProperties] { + let request = OSSystemExtensionRequest.propertiesRequest(forExtensionWithIdentifier: FilePath.packageName + ".system", queue: .main) + request.delegate = self + OSSystemExtensionManager.shared.submitRequest(request) + semaphore.wait() + if let error { + throw error + } + return properties! + } + + public static func isInstalled() async -> Bool { + await (try? Task { + try await isInstalledBackground() + }.result.get()) == true + } + + public nonisolated static func isInstalledBackground() async throws -> Bool { + for _ in 0 ..< 3 { + do { + let propList = try SystemExtension().getProperties() + if propList.isEmpty { + return false + } + for extensionProp in propList { + if !extensionProp.isAwaitingUserApproval, !extensionProp.isUninstalling { + return true + } + } + } catch { + try await Task.sleep(nanoseconds: NSEC_PER_SEC) + } + } + return false + } + + public nonisolated static func install(forceUpdate: Bool = false, inBackground: Bool = false) async throws -> OSSystemExtensionRequest.Result? { + try await Task.detached { + try SystemExtension(forceUpdate, inBackground).activation() + }.result.get() + } + + public nonisolated static func uninstall() async throws -> OSSystemExtensionRequest.Result? { + try await Task.detached { + try SystemExtension().deactivation() + }.result.get() + } + } +#endif diff --git a/Library/Shared/Bundle+Version.swift b/Library/Shared/Bundle+Version.swift new file mode 100644 index 0000000000000000000000000000000000000000..d527e0fae15c206167805892eca42c910addf0c1 --- /dev/null +++ b/Library/Shared/Bundle+Version.swift @@ -0,0 +1,11 @@ +import Foundation + +extension Bundle { + var version: String { + infoDictionary?["CFBundleShortVersionString"] as? String ?? "unknown" + } + + var versionNumber: String { + infoDictionary?["CFBundleVersion"] as? String ?? "unknown" + } +} diff --git a/Library/Shared/Color+Extension.swift b/Library/Shared/Color+Extension.swift new file mode 100644 index 0000000000000000000000000000000000000000..3efa58c3ac0df7d9e1135825ce66f8445033340c --- /dev/null +++ b/Library/Shared/Color+Extension.swift @@ -0,0 +1,25 @@ +import Foundation +import SwiftUI +#if canImport(UIKit) + import UIKit +#elseif canImport(AppKit) + import AppKit +#endif + +public extension Color { + static var textColor: Color { + #if canImport(UIKit) + return Color(uiColor: .label) + #elseif canImport(AppKit) + return Color(nsColor: .textColor) + #endif + } + + static var linkColor: Color { + #if canImport(UIKit) + return Color(uiColor: .link) + #elseif canImport(AppKit) + return Color(nsColor: .linkColor) + #endif + } +} diff --git a/Library/Shared/FilePath.swift b/Library/Shared/FilePath.swift new file mode 100644 index 0000000000000000000000000000000000000000..8289138cb144a532e2abe25aa2de6e40d2901aec --- /dev/null +++ b/Library/Shared/FilePath.swift @@ -0,0 +1,60 @@ +import Foundation + +public enum FilePath { + #if !NEXT + public static let packageName = "io.nekohasekai.sfabeino" + #else + public static let packageName = "io.nekohasekai.sfabeino.next" + #endif +} + +public extension FilePath { + static let groupName = "group.\(packageName)" + + private static let defaultSharedDirectory: URL! = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: FilePath.groupName) + + #if os(iOS) + static let sharedDirectory = defaultSharedDirectory! + #elseif os(tvOS) + static let sharedDirectory = defaultSharedDirectory + .appendingPathComponent("Library", isDirectory: true) + .appendingPathComponent("Caches", isDirectory: true) + #elseif os(macOS) + static var sharedDirectory: URL! = defaultSharedDirectory + #endif + + #if os(iOS) + static let cacheDirectory = sharedDirectory + .appendingPathComponent("Library", isDirectory: true) + .appendingPathComponent("Caches", isDirectory: true) + #elseif os(tvOS) + static let cacheDirectory = sharedDirectory + #elseif os(macOS) + static var cacheDirectory: URL { + sharedDirectory + .appendingPathComponent("Library", isDirectory: true) + .appendingPathComponent("Caches", isDirectory: true) + } + #endif + + #if os(macOS) + static var workingDirectory: URL { + cacheDirectory.appendingPathComponent("Working", isDirectory: true) + } + #else + static let workingDirectory = cacheDirectory.appendingPathComponent("Working", isDirectory: true) + + #endif + + static var iCloudDirectory = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents", isDirectory: true) ?? URL(string: "stub")! +} + +public extension URL { + var fileName: String { + var path = relativePath + if let index = path.lastIndex(of: "/") { + path = String(path[path.index(index, offsetBy: 1)...]) + } + return path + } +} diff --git a/Library/Shared/Variant.swift b/Library/Shared/Variant.swift new file mode 100644 index 0000000000000000000000000000000000000000..a894d12788883e69ca9ff797e6c56ffd4f115093 --- /dev/null +++ b/Library/Shared/Variant.swift @@ -0,0 +1,17 @@ +import Foundation + +public enum Variant { + #if os(macOS) + public static var useSystemExtension = false + #else + public static let useSystemExtension = false + #endif + + #if os(iOS) + public static let applicationName = "SFI" + #elseif os(macOS) + public static let applicationName = "SFM" + #elseif os(tvOS) + public static let applicationName = "SFT" + #endif +} diff --git a/MacLibrary/ApplicationDelegate.swift b/MacLibrary/ApplicationDelegate.swift new file mode 100644 index 0000000000000000000000000000000000000000..9cbe221ba15bce82e952bdcbe09c1a97fc390a06 --- /dev/null +++ b/MacLibrary/ApplicationDelegate.swift @@ -0,0 +1,48 @@ +import AppKit +import ApplicationLibrary +import Foundation +import Libbox +import Library + +open class ApplicationDelegate: NSObject, NSApplicationDelegate { + public func applicationDidFinishLaunching(_: Notification) { + NSLog("Here I stand") + LibboxSetup(FilePath.sharedDirectory.relativePath, FilePath.workingDirectory.relativePath, FilePath.cacheDirectory.relativePath, false) + let event = NSAppleEventManager.shared().currentAppleEvent + let launchedAsLogInItem = + event?.eventID == kAEOpenApplication && + event?.paramDescriptor(forKeyword: keyAEPropData)?.enumCodeValue == keyAELaunchedAsLogInItem + if SharedPreferences.inDebug || !launchedAsLogInItem || !SharedPreferences.showMenuBarExtra.getBlocking() || !SharedPreferences.menuBarExtraInBackground.getBlocking() { + NSApp.setActivationPolicy(.regular) + NSApp.activate(ignoringOtherApps: true) + } else { + NSApp.windows.first?.close() + } + Task { + do { + try await ProfileUpdateTask.configure() + if launchedAsLogInItem { + if await SharedPreferences.startedByUser.get() { + if let profile = try await ExtensionProfile.load() { + try await profile.start() + } + } + } + } catch { + NSLog("application setup error: \(error.localizedDescription)") + } + } + } + + public func applicationShouldTerminateAfterLastWindowClosed(_: NSApplication) -> Bool { + SharedPreferences.inDebug || !SharedPreferences.menuBarExtraInBackground.getBlocking() + } + + public func applicationShouldHandleReopen(_: NSApplication, hasVisibleWindows flag: Bool) -> Bool { + if !flag, NSApp.activationPolicy() == .accessory { + NSApp.setActivationPolicy(.regular) + NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.dock").first?.activate() + } + return true + } +} diff --git a/MacLibrary/Assets.xcassets/AccentColor.colorset/Contents.json b/MacLibrary/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..eb8789700816459c1e1480e0b34781d9fb78a1ca --- /dev/null +++ b/MacLibrary/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MacLibrary/Assets.xcassets/AppIcon.appiconset/Contents.json b/MacLibrary/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..f4ac7bc911eb4caf72cc1b183595c02588428fab --- /dev/null +++ b/MacLibrary/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "filename" : "apple-16 1x.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "filename" : "apple-16 2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "filename" : "apple-32 1x.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "filename" : "apple-32 2x 1.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "filename" : "apple-128 1x.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "filename" : "apple-128 2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "filename" : "apple-256 1x.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "filename" : "apple-256 2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "filename" : "apple-512 1x.png", + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "filename" : "apple-512 2x.png", + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-128 1x.png b/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-128 1x.png new file mode 100644 index 0000000000000000000000000000000000000000..9b10cd708cc8a3f8b70dadbbabe1309ed32758e0 Binary files /dev/null and b/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-128 1x.png differ diff --git a/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-128 2x.png b/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-128 2x.png new file mode 100644 index 0000000000000000000000000000000000000000..21693a6431dfc6ad84a35ee7fddb9dee4619f52f Binary files /dev/null and b/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-128 2x.png differ diff --git a/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-16 1x.png b/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-16 1x.png new file mode 100644 index 0000000000000000000000000000000000000000..28df597520e053a34d633e32f0981dd5e23cb539 Binary files /dev/null and b/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-16 1x.png differ diff --git a/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-16 2x.png b/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-16 2x.png new file mode 100644 index 0000000000000000000000000000000000000000..6852edd6fd40b34c9950f477afe8e90dc25ee763 Binary files /dev/null and b/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-16 2x.png differ diff --git a/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-256 1x.png b/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-256 1x.png new file mode 100644 index 0000000000000000000000000000000000000000..db0a7f0915e252834c1c675efd0585aec6e18f3e Binary files /dev/null and b/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-256 1x.png differ diff --git a/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-256 2x.png b/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-256 2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4449735ffd6181d546ed6ff32a15d555b97ed5a7 Binary files /dev/null and b/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-256 2x.png differ diff --git a/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-32 1x.png b/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-32 1x.png new file mode 100644 index 0000000000000000000000000000000000000000..171cb5b14eb2c352fc52783c0ccba37820801d72 Binary files /dev/null and b/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-32 1x.png differ diff --git a/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-32 2x 1.png b/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-32 2x 1.png new file mode 100644 index 0000000000000000000000000000000000000000..1654da969206ee09c3d2df402c79b36d0f0906d0 Binary files /dev/null and b/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-32 2x 1.png differ diff --git a/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-512 1x.png b/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-512 1x.png new file mode 100644 index 0000000000000000000000000000000000000000..99e829a131cba7c8cf40422d872d9786aa131c6e Binary files /dev/null and b/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-512 1x.png differ diff --git a/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-512 2x.png b/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-512 2x.png new file mode 100644 index 0000000000000000000000000000000000000000..88a80d63a1b8d0c9f8183232927c5edf3d5f0177 Binary files /dev/null and b/MacLibrary/Assets.xcassets/AppIcon.appiconset/apple-512 2x.png differ diff --git a/MacLibrary/Assets.xcassets/Contents.json b/MacLibrary/Assets.xcassets/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..73c00596a7fca3f3d4bdd64053b69d86745f9e10 --- /dev/null +++ b/MacLibrary/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/MacLibrary/Assets.xcassets/MenuIcon.symbolset/Contents.json b/MacLibrary/Assets.xcassets/MenuIcon.symbolset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..e9af0e2f7c732e02543bac78851408632f92bb76 --- /dev/null +++ b/MacLibrary/Assets.xcassets/MenuIcon.symbolset/Contents.json @@ -0,0 +1,15 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "symbol-rendering-intent" : "template" + }, + "symbols" : [ + { + "filename" : "menu_icon.svg", + "idiom" : "universal" + } + ] +} diff --git a/MacLibrary/Assets.xcassets/MenuIcon.symbolset/menu_icon.svg b/MacLibrary/Assets.xcassets/MenuIcon.symbolset/menu_icon.svg new file mode 100644 index 0000000000000000000000000000000000000000..704fa23a08131284784892da3acb628942da2e9f --- /dev/null +++ b/MacLibrary/Assets.xcassets/MenuIcon.symbolset/menu_icon.svg @@ -0,0 +1,417 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 25.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [ + <!ENTITY ns_extend "http://ns.adobe.com/Extensibility/1.0/"> + <!ENTITY ns_ai "http://ns.adobe.com/AdobeIllustrator/10.0/"> + <!ENTITY ns_graphs "http://ns.adobe.com/Graphs/1.0/"> + <!ENTITY ns_vars "http://ns.adobe.com/Variables/1.0/"> + <!ENTITY ns_imrep "http://ns.adobe.com/ImageReplacement/1.0/"> + <!ENTITY ns_sfw "http://ns.adobe.com/SaveForWeb/1.0/"> + <!ENTITY ns_custom "http://ns.adobe.com/GenericCustomNamespace/1.0/"> + <!ENTITY ns_adobe_xpath "http://ns.adobe.com/XPath/1.0/"> +]> +<svg version="1.1" id="图层_1" xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;" + xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 3300 2200" + style="enable-background:new 0 0 3300 2200;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:#FFFFFF;} + .st1{fill:none;stroke:#000000;stroke-width:0.5;} + .st2{font-family:'Helvetica-Bold';} + .st3{font-size:13px;} + .st4{font-family:'Helvetica';} + .st5{fill:none;stroke:#00AEEF;stroke-width:0.5;} + .st6{fill:#27AAE1;} + .st7{fill:none;stroke:#27AAE1;stroke-width:0.5;} +</style> +<metadata> + <sfw xmlns="&ns_sfw;"> + <slices></slices> + <sliceSourceBounds bottomLeftOrigin="true" height="2200" width="3300" x="0" y="0"></sliceSourceBounds> + </sfw> +</metadata> +<g id="Notes"> + <rect id="artboard" class="st0" width="3300" height="2200"/> + <line class="st1" x1="263" y1="292" x2="3036" y2="292"/> + <text transform="matrix(1 0 0 1 263 322)" class="st2 st3">Weight/Scale Variations</text> + <text transform="matrix(1 0 0 1 534.0632935 322)" class="st4 st3">Ultralight</text> + <text transform="matrix(1 0 0 1 843.7774658 322)" class="st4 st3">Thin</text> + <text transform="matrix(1 0 0 1 1139.0350342 322)" class="st4 st3">Light</text> + <text transform="matrix(1 0 0 1 1427.0772705 322)" class="st4 st3">Regular</text> + <text transform="matrix(1 0 0 1 1723.4418945 322)" class="st4 st3">Medium</text> + <text transform="matrix(1 0 0 1 2016.171875 322)" class="st4 st3">Semibold</text> + <text transform="matrix(1 0 0 1 2326.970459 322)" class="st4 st3">Bold</text> + <text transform="matrix(1 0 0 1 2618.2658691 322)" class="st4 st3">Heavy</text> + <text transform="matrix(1 0 0 1 2917.5053711 322)" class="st4 st3">Black</text> + <line class="st1" x1="263" y1="1903" x2="3036" y2="1903"/> + <g transform="matrix(1 0 0 1 263 1933)"> + <path d="M9.2480497,0.830078c4.3066502,0,7.8906507-3.5742178,7.8906507-7.8808579 + c0-4.3066201-3.5938005-7.8808198-7.9004202-7.8808198c-4.2968702,0-7.8710904,3.5741997-7.8710904,7.8808198 + C1.36719-2.7441399,4.95117,0.830078,9.2480497,0.830078z M9.2480497-0.654297 + c-3.5449295,0-6.3769598-2.8417931-6.3769598-6.3964829c0-3.5547204,2.8222699-6.3965201,6.3671904-6.3965201 + c3.5547199,0,6.4062195,2.8417997,6.4062195,6.3965201C15.6444998-3.4960899,12.8027-0.654297,9.2480497-0.654297z + M5.6543002-7.0507798c0,0.4296899,0.3027296,0.7226596,0.7519498,0.7226596h2.0996103v2.1191401 + c0,0.43945,0.2929697,0.7421801,0.7226601,0.7421801c0.4492092,0,0.7421799-0.3027301,0.7421799-0.7421801v-2.1191401h2.1190996 + c0.4394999,0,0.7421999-0.2929697,0.7421999-0.7226596c0-0.4394503-0.3027-0.7421904-0.7421999-0.7421904H9.9707003v-2.1093698 + c0-0.4492598-0.2929707-0.7519598-0.7421799-0.7519598c-0.4296904,0-0.7226601,0.3027-0.7226601,0.7519598v2.1093698H6.40625 + C5.9570298-7.7929702,5.6543002-7.4902301,5.6543002-7.0507798z"/> + </g> + <g transform="matrix(1 0 0 1 281.867 1933)"> + <path d="M11.7089996,2.9101601c5.4491997,0,9.9609013-4.5214901,9.9609013-9.9609394 + c0-5.4492202-4.5215015-9.9609203-9.9707012-9.9609203c-5.4394293,0-9.951149,4.5116997-9.951149,9.9609203 + C1.7480503-1.61133,6.2695303,2.9101601,11.7089996,2.9101601z M11.7089996,1.25 + c-4.6093893,0-8.291029-3.6914101-8.291029-8.3007793c0-4.6094203,3.67187-8.3008194,8.281229-8.3008194 + c4.6093998,0,8.3106003,3.6913996,8.3106003,8.3008194C20.0098-2.4414101,16.3183994,1.25,11.7089996,1.25z M7.1777306-7.0507798 + C7.1777306-6.5722699,7.5097704-6.25,8.0078096-6.25h2.8710899v2.8808601c0,0.4882798,0.3320007,0.8300798,0.8106003,0.8300798 + c0.4882002,0,0.8299999-0.3320298,0.8299999-0.8300798V-6.25h2.8809004c0.4882994,0,0.8300991-0.3222699,0.8300991-0.8007798 + c0-0.4882803-0.3417997-0.83008-0.8300991-0.83008h-2.8809004v-2.87114c0-0.4980001-0.3417997-0.8397999-0.8299999-0.8397999 + c-0.4785995,0-0.8106003,0.3417997-0.8106003,0.8397999v2.87114H8.0078096 + C7.5097704-7.8808599,7.1777306-7.5390601,7.1777306-7.0507798z"/> + </g> + <g transform="matrix(1 0 0 1 305.646 1933)"> + <path d="M14.9707003,5.6640601c6.9628992,0,12.7245989-5.7617164,12.7245989-12.7148399 + c0-6.9629197-5.7714996-12.7246208-12.7343988-12.7246208c-6.9530907,0-12.70504,5.7617006-12.70504,12.7246208 + C2.2558601-0.0976562,8.01758,5.6640601,14.9707003,5.6640601z M14.9707003,3.8476601 + c-6.0351505,0-10.88867-4.8632803-10.88867-10.8984394c0-6.0449204,4.8437505-10.8984203,10.87887-10.8984203 + c6.0449991,0,10.9082003,4.8534994,10.9082003,10.8984203C25.8691006-1.01562,21.0156002,3.8476601,14.9707003,3.8476601z + M9.1992197-7.0507798c0,0.51758,0.3710899,0.8789001,0.9179802,0.8789001H14.0625V-2.2168 + c0,0.53711,0.3711004,0.9179699,0.8886995,0.9179699c0.5371008,0,0.9082003-0.3710899,0.9082003-0.9179699v-3.9550798h3.955101 + c0.5370998,0,0.9179001-0.36132,0.9179001-0.8789001c0-0.5468802-0.3711014-0.9179702-0.9179001-0.9179702h-3.955101v-3.9453497 + c0-0.5468006-0.3710995-0.9277-0.9082003-0.9277c-0.5175991,0-0.8886995,0.3808994-0.8886995,0.9277V-7.96875h-3.9453001 + C9.5703096-7.96875,9.1992197-7.5976601,9.1992197-7.0507798z"/> + </g> + <text transform="matrix(1 0 0 1 263 1953)" class="st2 st3">Design Variations</text> + <text transform="matrix(1 0 0 1 263 1971)" class="st4 st3">Symbols are supported in up to nine weights and three scales.</text> + <text transform="matrix(1 0 0 1 263 1989)" class="st4 st3">For optimal layout with text and other symbols, vertically align</text> + <text transform="matrix(1 0 0 1 263 2007)" class="st4 st3">symbols with the adjacent text.</text> + <line class="st5" x1="776" y1="1919" x2="776" y2="1933"/> + <g transform="matrix(1 0 0 1 776 1933)"> + <path d="M3.31055,0.15625c0.51757,0,0.7714798-0.1953125,0.9570301-0.742188l1.2597599-3.4472618h5.7617598l1.2597008,3.4472618 + c0.1855993,0.5468755,0.4394999,0.742188,0.9473,0.742188c0.5175991,0,0.8495998-0.3125,0.8495998-0.800781 + c0-0.166016-0.0293007-0.322266-0.1073999-0.527349L9.6582003-13.3690996c-0.2246103-0.5957003-0.625-0.8985004-1.25-0.8985004 + c-0.6054702,0-1.0156202,0.2930002-1.2304702,0.8887005L2.5976601-1.16211c-0.07813,0.205079-0.10743,0.361329-0.10743,0.527344 + C2.4902301-0.146484,2.8027301,0.15625,3.31055,0.15625z M6.0058599-5.51758l2.3730502-6.5722198h0.0488195L10.8008003-5.51758 + H6.0058599z"/> + </g> + <line class="st5" x1="793.1970215" y1="1919" x2="793.1970215" y2="1933"/> + <text transform="matrix(1 0 0 1 776 1953)" class="st2 st3">Margins</text> + <text transform="matrix(1 0 0 1 776 1971)" class="st4 st3">Leading and trailing margins on the left and right side of each symbol</text> + <text transform="matrix(1 0 0 1 776 1989)" class="st4 st3">can be adjusted by modifying the x-location of the margin guidelines.</text> + <text transform="matrix(1 0 0 1 776 2007)" class="st4 st3">Modifications are automatically applied proportionally to all</text> + <text transform="matrix(1 0 0 1 776 2025)" class="st4 st3">scales and weights.</text> + <g transform="matrix(1 0 0 1 1289 1933)"> + <path d="M2.8418,1.86523l1.6992199,1.70899c0.8593702,0.8691399,1.8457003,0.81055,2.7734299-0.2148399L18.0077991-8.4277296 + l-0.9667988-0.9765701L6.4257798,2.2753899c-0.3515596,0.4003901-0.6835899,0.49805-1.1523399,0.0293L4.1015601,1.14258 + c-0.46875-0.458986-0.36133-0.800783,0.0390601-1.1621113L15.6152-10.8203001l-0.9765005-0.9667997L3.04688-0.898438 + C2.06055,0.0195312,1.98242,0.996094,2.8418,1.86523z M9.2578096-16.3281002 + c-0.41992,0.4101-0.4492197,0.9863005-0.2148399,1.3769007c0.2343702,0.3613997,0.6933603,0.5956993,1.3379307,0.4296999 + c1.4647999-0.3418007,2.9881992-0.4004002,4.4139996,0.5370998l-0.5859003,1.4551001 + c-0.3417997,0.8301001-0.1659994,1.4159994,0.3711004,1.9629002L16.875-8.2519503 + c0.4883003,0.4882803,0.8983994,0.5078101,1.4647999,0.4101501l1.0645008-0.1953101l0.6640987,0.6738305l-0.0390987,0.5566397 + c-0.0391006,0.4980502,0.0879002,0.8789101,0.5761986,1.35742l0.7617016,0.7421904 + c0.4784985,0.4785099,1.0936985,0.5078096,1.5625,0.0390596l2.910099-2.91992 + c0.4687996-0.4687495,0.4493008-1.0644498-0.0293007-1.5429702L25.0391006-9.89258 + c-0.4786015-0.4785204-0.8495998-0.6347198-1.3282013-0.5957203L23.1348-10.4394999L22.4902-11.0741997L22.7343998-12.1973 + c0.1268997-0.5663996-0.0293007-1.0058002-0.6152992-1.5917997l-2.1972008-2.1875 + C16.5820007-19.2968998,12.1484003-19.2187996,9.2578096-16.3281002z M10.7519999-15.9569998 + c2.4316006-1.7774,5.7226-1.4746008,7.9492006,0.7518997l2.4315987,2.4120998 + c0.2344017,0.2343998,0.2734013,0.4200001,0.205101,0.7617998l-0.3223,1.4843006L22.5195007-9.0625l0.9863987-0.0585899 + C23.7598-9.1308603,23.8379002-9.11133,24.0331993-8.9160204l0.5762005,0.5761805l-2.4414005,2.4414001l-0.5761986-0.57617 + c-0.1953011-0.1953101-0.2245998-0.2734404-0.2147999-0.5371103l0.0682983-0.9765596l-1.4940987-1.4843802L18.4277-9.21875 + c-0.3222008,0.0683603-0.4687004,0.0390596-0.7129002-0.1953096l-2.0018997-2.0019407 + c-0.2539005-0.2343998-0.2832003-0.4003992-0.1269999-0.7714996l0.8788996-2.0897999 + c-1.5625-1.4551001-3.5936995-2.080101-5.625-1.4843998C10.6836004-15.7227001,10.625-15.8495998,10.7519999-15.9569998z"/> + </g> + <text transform="matrix(1 0 0 1 1289 1953)" class="st2 st3">Exporting</text> + <text transform="matrix(1 0 0 1 1289 1971)" class="st4 st3">Symbols should be outlined when exporting to ensure the</text> + <text transform="matrix(1 0 0 1 1289 1989)" class="st4 st3">design is preserved when submitting to Xcode.</text> + <text id="template-version" transform="matrix(1 0 0 1 2952.4140625 1933)" class="st4 st3">Template v.4.0</text> + <text transform="matrix(1 0 0 1 2865.4575195 1951)" class="st4 st3">Requires Xcode 14 or greater</text> + <text id="descriptive-name" transform="matrix(1 0 0 1 2865.4702148 1969)" class="st4 st3">Generated from singbox-filled</text> + <text transform="matrix(1 0 0 1 2912.4111328 1987)" class="st4 st3">Typeset at 100 points</text> + <text transform="matrix(1 0 0 1 263 726)" class="st4 st3">Small</text> + <text transform="matrix(1 0 0 1 263 1156)" class="st4 st3">Medium</text> + <text transform="matrix(1 0 0 1 263 1586)" class="st4 st3">Large</text> +</g> +<g id="Guides"> + <g id="H-reference" transform="matrix(1 0 0 1 339 696)"> + <path class="st6" d="M0.993654,0h2.6440959l25.6903496-67.1323013h0.7021999v-3.3266983h-1.9076996L0.993654,0z + M11.6885004-24.4799004h35.2929993l-0.75-2.2486H12.4385004L11.6885004-24.4799004z M55.1195984,0h2.6441002 + L30.6382008-70.4589996H29.4326v3.3266983L55.1195984,0z"/> + </g> + <line id="Baseline-S" class="st7" x1="263" y1="696" x2="3036" y2="696"/> + <line id="Capline-S" class="st7" x1="263" y1="625.5410156" x2="3036" y2="625.5410156"/> + <g id="H-reference_1_" transform="matrix(1 0 0 1 339 1126)"> + <path class="st6" d="M0.993654,0h2.6440959l25.6903496-67.1323013h0.7021999v-3.3266983h-1.9076996L0.993654,0z + M11.6885004-24.4799004h35.2929993l-0.75-2.2486H12.4385004L11.6885004-24.4799004z M55.1195984,0h2.6441002 + L30.6382008-70.4589996H29.4326v3.3266983L55.1195984,0z"/> + </g> + <line id="Baseline-M" class="st7" x1="263" y1="1126" x2="3036" y2="1126"/> + <line id="Capline-M" class="st7" x1="263" y1="1055.5400391" x2="3036" y2="1055.5400391"/> + <g id="H-reference_2_" transform="matrix(1 0 0 1 339 1556)"> + <path class="st6" d="M0.993654,0h2.6440959l25.6903496-67.1323013h0.7021999v-3.3266983h-1.9076996L0.993654,0z + M11.6885004-24.4799004h35.2929993l-0.75-2.2486H12.4385004L11.6885004-24.4799004z M55.1195984,0h2.6441002 + L30.6382008-70.4589996H29.4326v3.3266983L55.1195984,0z"/> + </g> + <line id="Baseline-L" class="st7" x1="263" y1="1556" x2="3036" y2="1556"/> + <line id="Capline-L" class="st7" x1="263" y1="1485.5400391" x2="3036" y2="1485.5400391"/> + <line id="left-margin-Ultralight-S" class="st5" x1="512.4609985" y1="600.7849731" x2="512.4609985" y2="720.1209717"/> + <line id="right-margin-Ultralight-S" class="st5" x1="606.9609985" y1="600.7849731" x2="606.9609985" y2="720.1209717"/> + <line id="left-margin-Regular-S" class="st5" x1="1401.1899414" y1="600.7849731" x2="1401.1899414" y2="720.1209717"/> + <line id="right-margin-Regular-S" class="st5" x1="1498.4899902" y1="600.7849731" x2="1498.4899902" y2="720.1209717"/> + <line id="left-margin-Black-S" class="st5" x1="2883" y1="600.7849731" x2="2883" y2="720.1209717"/> + <line id="right-margin-Black-S" class="st5" x1="2983.8000488" y1="600.7849731" x2="2983.8000488" y2="720.1209717"/> + <line id="left-margin-Regular-M" class="st5" x1="1388.0899658" y1="1030.7900391" x2="1388.0899658" y2="1150.1199951"/> + <line id="right-margin-Regular-M" class="st5" x1="1511.5899658" y1="1030.7900391" x2="1511.5899658" y2="1150.1199951"/> +</g> +<g id="Symbols"> + <g id="Black-L" transform="matrix(1 0 0 1 2851.12 1556)"> + <path d="M84.4896164-97.2925034c4.4400101-2.3999939,9-2.3999939,13.5600052,0l40.5599976,21.8399963l-12.2399979,6.6000061 + L79.0896225-94.2925034L84.4896164-97.2925034z M80.1696243,22.3474998L35.8896179-1.8925002 + c-3.3599968-1.9200003-4.6799965-3.9600003-4.6799965-7.3200006v-48.7200012l48.8400002,26.2800026L80.1696243,22.3474998 + L80.1696243,22.3474998z M90.1296234-50.6124992L44.4096222-75.8125l13.7999992-6.2400055l45.9600029,25.0800018 + L90.1296234-50.6124992z M149.8896332-9.3324995c0,3.3599997-1.3200073,5.3999996-4.6800232,7.3199987l-44.1599884,24.1200008 + v-53.5199966l13.3199997-6.7200012v8.3999977c0,6.3600025,4.9199982,11.6400013,11.2799988,11.880003 + c6.7199936,0.3599987,12.4800034-5.0400009,12.4800034-11.7600021v-21.2399998l11.8799896-6.4799995L149.8896332-9.3324995 + L149.8896332-9.3324995z"/> + </g> + <g id="Heavy-L" transform="matrix(1 0 0 1 2555.07 1556)"> + <path d="M83.3082047-98.7324982c4.6800079-2.640007,9.4800034-2.640007,14.2800064,0l43.1994019,23.6399994L125.9076157-66.8125 + L75.5082092-94.2925034L83.3082047-98.7324982z M80.6682129,24.7474995L33.9882088-1.0525004 + c-3.5999985-2.04-4.9199982-4.0799994-4.9199982-7.7999997V-59.732502l51.4799995,28.0800018v56.4000015H80.6682129z + M90.5082092-47.9725037L39.0282097-75.5725021l20.8800011-9.9599915l50.3999939,27.4799957L90.5082092-47.9725037z + M150.7476196-8.9724998c0,3.6000004-1.3200073,5.7599993-4.9200134,7.7999997L99.2682114,24.5074997v-56.0400009 + l15.9593964-8.4000015v11.6400032c0,5.5200005,4.0800018,9.9599991,9.6000061,10.3199997 + c5.9999924,0.6000004,11.159996-4.2000008,11.159996-10.2000008v-23.0399971l14.7600098-8.2800026V-8.9724998z"/> + </g> + <g id="Bold-L" transform="matrix(1 0 0 1 2259.18 1556)"> + <path d="M81.466217-98.9458237c4.9199982-2.7599945,9.7200012-2.8800049,14.640007,0l46.5602341,25.7999954 + l-18.2399979,10.2000008L70.3062286-92.5858231L81.466217-98.9458237z M80.5062256,29.5741749L31.0662193,1.9741741 + c-3.9599991-2.2799995-5.2799988-4.4399996-5.2799988-8.2799997v-53.7600021l54.720005,30.2400017V29.5741749z + M88.6662216-44.345829L34.7862244-74.2258301l21.8399963-10.4399948l53.159996,29.3999977L88.6662216-44.345829z + M150.9464722-6.4258256c0,3.8399994-1.4400177,6-5.280014,8.2799997L96.2262268,29.3341751v-59.0400009l19.2002335-10.4400024 + v15.7200031c0,4.3199978,3.2399979,8.039999,7.5600052,8.5199986c5.1599884,0.7200003,9.6000061-3.2399979,9.6000061-8.3999996 + V-49.505825l18.4799805-10.3199997v53.3999977H150.9464722z"/> + </g> + <g id="Semibold-L" transform="matrix(1 0 0 1 1963.01 1556)"> + <path d="M80.2488403-102.0924988c5.0400009-2.8800049,9.8399963-3,14.8800049,0l48.9603424,27.3600006l-20.519989,11.5200005 + l-56.640358-31.3199959L80.2488403-102.0924988z M80.6088333,29.7874985L29.0088406,0.9875 + C24.8088379-1.4125,23.4888401-3.6925001,23.4888401-7.6525002v-55.7999992l57.1199951,31.9199982V29.7874985z + M90.2088394-44.732502l-57-31.7999916l21.8400002-11.5200119l56.6403542,31.3200035L90.2088394-44.732502z + M151.2891998-7.7724996c0,4.0799994-1.4400024,6.2399998-5.519989,8.6399994L94.1688385,29.5475006v-61.0800018 + l21.4803619-11.8799973v18.4799976c0,3.6000004,2.6399918,6.7199993,6.1199951,7.1999989 + c4.5600052,0.8400021,8.3999939-2.6399975,8.3999939-7.079998v-26.6399994l21.0000153-11.7599983L151.2891998-7.7724996 + L151.2891998-7.7724996z"/> + </g> + <g id="Medium-L" transform="matrix(1 0 0 1 1666.71 1556)"> + <path d="M74.419426-103.0525055c5.0400085-2.8799896,9.9600067-3,15,0l50.759758,28.4400101l-22.3199997,12.4799957 + L59.4194298-94.5324936L74.419426-103.0525055z M75.7394257,31.3474998L22.5794296,1.7074999 + c-4.3199997-2.52-5.6400013-4.8000002-5.6400013-8.8799992v-57.2399979l58.6800003,33v62.7599983H75.7394257z + M82.9394302-43.0524979L24.139431-76.0525055l22.2000008-11.5199966l59.1597595,31.6800003L82.9394302-43.0524979z + M146.5391846-7.4125004c0,4.1999998-1.4399872,6.3600001-5.6399994,8.8800001L87.859436,31.1075001v-62.6399994 + l23.1597519-12.9599991v20.6400013c0,3,2.159996,5.7599983,5.159996,6.2399979 + c4.0800018,0.8400021,7.5600052-2.1599979,7.5600052-6.1200008v-27.8400002l22.9200058-12.9599991v57.1200027H146.5391846z"/> + </g> + <g id="Regular-L" transform="matrix(1 0 0 1 1370.57 1556)"> + <path d="M74.4695053-104.3725052c5.1600037-3,10.0800018-3.1199951,15.2400055,0l53.0404739,30l-24.7200012,13.6800041 + L57.1895065-94.652504L74.4695053-104.3725052z M76.989502,33.3875008L21.9095058,2.4274998 + C17.3495064-0.2125,16.0295067-2.6125007,16.0295067-6.8125005v-59.1600037L76.989502-31.5325012V33.3875008z + M81.9095078-38.8525009L20.9495068-73.1725006l29.1599979-15.7200012l61.2004852,33.7200012L81.9095078-38.8525009z + M148.1499939-6.9324999c0,4.3199992-1.3200073,6.5999999-5.8800201,9.2399998L87.1895065,33.267498v-64.6799927 + l25.4404755-14.2800026v23.4000015c0,2.2799988,1.4400101,4.3199997,3.6000061,5.0400009 + c3.3600006,0.9599991,6.4799957-1.5600014,6.4799957-4.920002v-29.2799988l25.4400101-14.2799988V-6.9324999 + L148.1499939-6.9324999z"/> + </g> + <g id="Light-L" transform="matrix(1 0 0 1 1074.4 1556)"> + <path d="M75.4522476-106.0525055c4.3199921-2.5199966,8.5199966-2.6399918,12.7199936,0l57.3597565,32.0400085l-27.1199951,15 + L54.2122421-94.4124985L75.4522476-106.0525055z M78.0922394,35.3074989L20.6122437,3.6274998 + c-4.9200001-2.7599998-6-5.04-6-9.2400007v-61.6800041l63.4799957,35.2800064V35.3074989z M16.8922424-73.5324936 + l31.1999969-17.640007l63.1197586,36L81.3322449-38.3725014L16.8922424-73.5324936z M149.2519989-5.7325001 + c0,4.3200002-1.1999969,6.4799995-6,9.2399998L85.7722397,35.1875v-67.1999969l27.9597626-15.4800034v26.1599998 + c0,1.7999992,1.3200073,3.3600006,3,3.7199993c2.5200043,0.6000004,4.6800003-1.1999989,4.6800003-3.7199993v-30.6000023 + l27.9599915-15.4799957v61.6800003H149.2519989z"/> + </g> + <g id="Thin-L" transform="matrix(1 0 0 1 778.505 1556)"> + <path d="M76.3855591-108.452507c3-1.7999954,6.3600006-1.7999954,9.3600006,0l63.0002441,34.8000031l-30.6000061,16.5600052 + L50.1055603-93.9324951L76.3855591-108.452507z M79.3855591,37.9475021L18.6655579,5.0674996 + C13.3855591,2.1875,12.545558,0.1474997,12.545558-4.1724997v-65.0400009l66.840004,36.3600006V37.9475021z + M15.4255581-74.1324997L46.0255585-90.8125l66.7202377,36L81.7855606-37.4124985L15.4255581-74.1324997z M150.5457916-4.1724997 + c0,4.3199992-0.8399963,6.3599997-6,9.2399998L83.7055588,37.8274994V-32.732502l31.2002411-16.9199982v29.8800011 + c0,1.1999989,0.9599991,2.0399971,2.0400009,2.1599979c1.3199997,0,2.2799988-0.9599991,2.2799988-2.1599979v-32.2799988 + l31.1999969-16.9200058v64.8000183C150.4257965-4.1724882,150.5457916-4.1724882,150.5457916-4.1724997z"/> + </g> + <g id="Ultralight-L" transform="matrix(1 0 0 1 485.519 1556)"> + <path d="M66.7952805-109.5324936c2.4000015-1.4400101,5.159996-1.4400101,7.5599976,0l65.8801193,36.2399902 + l-32.159996,17.2800064L37.9952774-93.6924973L66.7952805-109.5324936z M69.9152832,39.267498L7.5152793,5.9074998 + C1.99528,3.0274997,1.39528,0.9875,1.39528-3.3325v-66.7200089l68.5200043,36.9600067V39.267498z M2.1152804-73.0525055 + l33.6000023-18.7199936l69.6000061,37.7999954l-34.4400101,18.720005L2.1152804-73.0525055z M141.0753937-3.4525003 + c0,4.3200006-0.6000061,6.3600001-6.1199951,9.2399998L72.555275,39.0275002v-72.1199951l33-17.7600021v31.8000011 + c0.1200027,0.8399982,0.840126,1.3199978,1.5601273,1.3199978c0.7199936-0.2399979,1.0800018-0.7199974,1.0800018-1.4399986 + v-33.1199989l33-17.760006L141.0753937-3.4525003L141.0753937-3.4525003z"/> + </g> + <g id="Black-M" transform="matrix(1 0 0 1 2869.28 1126)"> + <path d="M67.9602814-85.0525055c3.1200027-1.6799927,6.4800034-1.6799927,9.5999985,0l29.8800049,15.7200012l-7.5599976,3.9599991 + L65.320282-83.7324982L67.9602814-85.0525055z M63.2802811,4.7075L30.7602844-12.9324999 + c-2.3999996-1.3200006-3.3600006-2.8800011-3.3600006-5.2800016v-36.1199989l36,18.8400002L63.2802811,4.7075L63.2802811,4.7075z + M73.0002823-50.9725037L37.9602852-69.3325043l8.279995-4.6799927l35.2800064,18.239994L73.0002823-50.9725037z + M117.0403976-18.3325005c0,2.3999996-0.9599915,3.960001-3.3601074,5.2799997L81.0402832,4.4674997v-39.8400002 + l8.4000015-3.9599991v3.9599991c0,5.6399994,4.5599976,10.2000008,10.1999969,10.3200016 + c5.7600021,0,10.4400024-4.4400005,10.4400024-10.1999989v-15l6.9601135-3.720005V-18.3325005z"/> + </g> + <g id="Heavy-M" transform="matrix(1 0 0 1 2573.14 1126)"> + <path d="M66.8856506-86.3725052c3.4800034-1.9199982,6.9600067-1.9199982,10.5600052,0l31.9199982,17.2800064 + l-9.8399963,5.3999977L62.205658-83.8525009L66.8856506-86.3725052z M63.7656555,6.8674998L29.0856552-12.0924997 + c-2.6399994-1.4400005-3.6000004-3-3.6000004-5.6400023v-38.0400009l38.2799988,20.5200043V6.8674998z M71.8056564-49.4124985 + L37.6056557-68.8525009l10.079998-5.159996l34.1999969,19.4399948L71.8056564-49.4124985z M117.7651672-17.9724998 + c0,2.6399984-1.0799866,4.1999998-3.5995102,5.6400003L79.4856567,6.7474999v-41.8800011l10.6800003-5.4000015v6.9600029 + c0,4.7999992,3.7199936,8.7599983,8.5199966,8.8799973c5.0400085,0.3600006,9.3600006-3.7199993,9.3600006-8.8799973v-16.6800003 + l9.5995178-5.2800026v37.5600014H117.7651672z"/> + </g> + <g id="Bold-M" transform="matrix(1 0 0 1 2276.99 1126)"> + <path d="M64.9717941-86.1058197c3.8399963-2.1600037,7.5600052-2.1600037,11.5199966,0l34.3199997,18.9599915 + L98.211792-60.1858253L58.131794-82.1458282L64.9717941-86.1058197z M63.6517944,11.0941744L26.6917934-9.4258261 + c-2.8799992-1.6799994-3.960001-3.3599997-3.960001-6.2399998v-40.2000008l40.920002,22.5599976V11.0941744z + M69.8917923-44.9458275L30.2917938-66.6658249l15.9600029-8.7600021l39.5999947,21.840004L69.8917923-44.9458275z + M118.011673-15.7858257c0,2.8799992-1.0799942,4.5599995-3.959877,6.2400007L77.211792,10.9741745v-44.2800026 + l13.3200073-7.1999969v10.3199997c0,3.8400002,2.8799896,7.079998,6.7199936,7.4400005 + c4.4399948,0.6000004,8.2800064-2.8799992,8.2800064-7.3200016v-18.5999985l12.5998764-7.0800018v39.9599991H118.011673z"/> + </g> + <g id="Semibold-M" transform="matrix(1 0 0 1 1980.77 1126)"> + <path d="M63.6641197-88.8925018c4.0800056-2.2799988,8.0399971-2.4000015,12.1199989,0l35.8800049,20.0400009L97.264122-60.8125 + L55.1441231-84.0924988L63.6641197-88.8925018z M63.4241219,10.9474993L24.9041195-10.6525002 + c-3.119997-1.8000002-4.0799999-3.6000004-4.0799999-6.5999985v-41.6400032l42.720005,23.8800049L63.4241219,10.9474993 + L63.4241219,10.9474993z M69.6641235-45.8125l-42-22.4400024l17.7600002-10.3199997l43.3200035,22.5600052L69.6641235-45.8125z + M118.0239944-17.2524986c0,2.9999981-1.1999969,4.7999983-4.0798721,6.5999985L75.4241257,10.9474993v-45.8400002 + l15.1199951-8.3999977v12.5999985c0,3.2400017,2.2800064,6,5.5199966,6.3600006 + c3.9600067,0.7199993,7.4400024-2.4000015,7.4400024-6.2399998v-19.9200001l14.6398849-8.2800026L118.0239944-17.2524986 + L118.0239944-17.2524986z"/> + </g> + <g id="Medium-M" transform="matrix(1 0 0 1 1684.38 1126)"> + <path d="M57.999958-89.7324982c4.3199997-2.4000015,8.2800026-2.5200043,12.6000023,0l37.0800018,21l-15.840004,8.8799973 + L48.3999596-84.0924988L57.999958-89.7324982z M58.5999565,12.2674999L18.8799572-10.1724997 + c-3.2399979-1.9200001-4.3199997-3.7200003-4.3199997-6.8400011v-42.9600029l44.0400009,24.840004V12.2674999z + M63.3999596-44.1324997L21.7599564-67.5325012l19.2000008-10.5599976L83.4399567-54.8125L63.3999596-44.1324997z + M113.4395981-17.1325016c0,3.2400017-1.1999969,4.920001-4.3199997,6.8400011L69.3999557,12.1475v-47.159996 + l16.4400024-9.2400017v14.2799988c0,2.6399994,1.7999954,5.1599998,4.5599976,5.5200005 + c3.6000061,0.8399982,6.8399963-1.9200001,6.8399963-5.5200005V-50.732502l16.199646-9.2400017V-17.1325016z"/> + </g> + <g id="Regular-M" transform="matrix(1 0 0 1 1388.09 1126)"> + <path d="M58.0215912-90.6924973c4.5599976-2.6399994,8.7600021-2.6399994,13.1999969,0l38.640007,21.9599991l-17.640007,9.9599953 + L46.8615913-84.3324966L58.0215912-90.6924973z M59.8215904,13.9474993L18.5415916-9.5724993 + c-3.3600016-2.0400009-4.5600023-3.8400011-4.5600023-7.1999998v-44.4000015l45.8400002,26.2799988V13.9474993z + M64.9815903-43.4124985L21.5415916-68.6124954l17.8799973-10.4400101L85.0215836-53.732502L64.9815903-43.4124985z + M115.0215836-16.7724991c0,3.3599987-1.1999969,5.1599989-4.5599899,7.1999998L69.1815872,13.8274994v-48.7200012 + l18.2399979-10.4399986v16.5600014c0,2.0399971,1.3200073,3.9599991,3.2400055,4.5599976 + c3.1200027,0.9600029,6.1200027-1.4399986,6.1200027-4.4399986v-21.9599991l18.2399902-10.4399986V-16.7724991z"/> + </g> + <g id="Light-M" transform="matrix(1 0 0 1 1091.9 1126)"> + <path d="M58.9000015-92.2525024c3.7200012-2.1601181,7.3199997-2.2801208,10.9200058,0l42.4799881,23.8799973 + L92.3801193-57.3326187L44.2601204-83.9725037L58.9000015-92.2525024z M60.8199997,15.7474403L17.3801212-8.4925003 + c-3.5999994-2.1599998-4.5601177-3.96-4.5601177-7.1999998v-46.6800003l47.9999962,27V15.7474403z M116.0200043-15.812501 + c0,3.3601208-0.9599991,5.0400009-4.5600052,7.2000008L67.8998795,15.5074997v-51l20.5200043-11.4000015V-27.8125 + c0,1.6799984,1.0799942,3,2.6398773,3.4799995c2.4000015,0.5999985,4.4400024-1.0799999,4.4400024-3.3600006v-23.1599998 + l20.5202408-11.5200005V-15.812501L116.0200043-15.812501z M84.6999969-53.8525009L64.4199982-42.932621L18.2200031-69.0926208 + l20.16012-11.1601181L84.6999969-53.8525009z"/> + </g> + <g id="Thin-M" transform="matrix(1 0 0 1 795.884 1126)"> + <path d="M59.7479095-94.2925034c2.6400032-1.5599976,5.4000015-1.5599976,7.9200058,0l47.5204773,26.4000015 + L92.2679062-55.4124985L40.5479126-83.6124954L59.7479095-94.2925034z M62.0279121,18.0275002L15.7079124-7.1724997 + c-3.9599991-2.1599998-4.6800003-3.8400011-4.6800003-7.1999998v-49.6800003l51,27.9599991V18.0275002z M12.7079124-68.1324997 + l23.6399994-12.5999985L87.347908-52.5325012L64.7879105-39.9325027L12.7079124-68.1324997z M117.1083908-14.3724995 + c0,3.3599987-0.7200012,5.04-4.6800003,7.1999998l-46.3204803,25.079998v-53.8800049L89.6279144-48.8125v22.3199997 + c0,1.0799999,0.8399963,1.7999992,1.7999954,2.0400009c1.3199997,0.1199989,2.2800064-0.8400002,2.2800064-2.0400009v-24.5999985 + l23.5204697-12.840004L117.1083908-14.3724995L117.1083908-14.3724995z"/> + </g> + <g id="Ultralight-M" transform="matrix(1 0 0 1 500.783 1126)"> + <path d="M54.806797-95.3725052c2.0399971-1.1999969,4.3199997-1.1999969,6.3600006,0l50.1593895,27.6000061L86.9666748-54.5725021 + L33.3267937-83.4925003L54.806797-95.3725052z M57.2067986,19.2275009L9.4467955-6.4525003 + c-4.2000012-2.2799997-4.6800008-3.8400002-4.6800008-7.1999998v-51.1199989l52.5599976,28.4399986L57.2067986,19.2275009 + L57.2067986,19.2275009z M5.4867954-68.1324997L30.806797-82.0525055l53.1599922,29.4000053L58.7667961-39.2124977 + L5.4867954-68.1324997z M112.406189-13.6525002c0,3.3599997-0.4799957,4.9200001-4.6793976,7.1999998L59.8467941,19.1075001 + v-55.4400024l24.9599991-13.5600014v24.0000019c0,0.7199993,0.7200012,1.1999989,1.3199997,1.1999989 + c0.7200012-0.1199989,1.2000046-0.5999985,1.2000046-1.3199997v-25.3199997l24.9593964-13.5600014v51.2400017H112.406189z"/> + </g> + <g id="Black-S" transform="matrix(1 0 0 1 2883 696)"> + <path d="M55.3755989-70.4925003c2.2799988-1.1999969,4.7999992-1.1999969,7.2000008,0l23.0399933,12l-5.2799988,2.7599983 + L53.6955986-69.652504L55.3755989-70.4925003z M51.2955971-1.0125005L26.2155972-14.4524994 + c-1.7999992-0.960001-2.5200005-2.0400009-2.5200005-3.960001v-27.8399982l27.6000004,14.2799969V-1.0125005z + M58.9755974-44.6925011L32.335598-58.6124992l6-3.1200027l26.7599983,13.7999992L58.9755974-44.6925011z M93.2956009-18.5325012 + c0,1.7999992-0.7200012,3-2.5200043,3.9600019L65.6955948-1.1324999V-31.852499l6-2.6400013v2.4000015 + c0,4.6799984,3.7200012,8.3999977,8.4000015,8.3999977c4.5600052,0,8.4000015-3.7199993,8.4000015-8.3999977v-11.4000015 + l4.8000031-2.5199966V-18.5325012z"/> + </g> + <g id="Heavy-S" transform="matrix(1 0 0 1 2586.68 696)"> + <path d="M54.3992653-71.452507c2.6399994-1.4399948,5.3999977-1.4399948,8.159996,0l24.239994,12.9600067l-7.0799866,3.8400002 + L51.3992653-69.8925018L54.3992653-71.452507z M51.5192604,0.6675001L24.9992638-13.852499 + c-2.0400009-1.0800018-2.7600021-2.3999996-2.7600021-4.3200016v-29.2799988L51.5192604-31.732502V0.6675001z + M58.4792671-43.2524986L30.0392628-58.4925003l7.6799984-4.2000008l28.4400024,15.1199989L58.4792671-43.2524986z + M93.8792572-18.2924995c0,2.0400009-0.8399963,3.2400007-2.7599945,4.3199997L64.4792633,0.5474997v-32.1599998 + l7.6800003-3.8400002v4.5599995c0,3.9599991,3.1199951,7.3199997,7.0800018,7.4400005 + c4.1999969,0.2399979,7.6800003-3.1200008,7.6800003-7.3200035v-12.5999985l6.8399963-3.7199974L93.8792572-18.2924995 + L93.8792572-18.2924995z"/> + </g> + <g id="Bold-S" transform="matrix(1 0 0 1 2290.45 696)"> + <path d="M52.662468-70.5858231c3.1199989-1.6800003,6.239994-1.8000031,9.3599968,0l25.6799965,14.1599998l-9,5.0400009 + L48.2224655-68.1858215L52.662468-70.5858231z M51.2224655,4.2941747L23.0224667-11.4258251 + c-2.1600018-1.3199997-3-2.6400003-3-4.7999992v-30.8400021l31.1999989,17.2800026V4.2941747z M57.2224655-39.7458267 + L26.9824657-56.4258232l9.4800014-5.2800026L66.822464-45.0258255L57.2224655-39.7458267z M93.822464-16.3458252 + c0,2.1599989-0.8399963,3.6000004-3,4.8000002L62.622467,4.1741743v-33.8400002l9.5999985-5.1599998v7.0799999 + c0,3.2399979,2.3999939,6,5.6399994,6.2399979c3.7200012,0.4800014,6.9599991-2.5199986,6.9599991-6.119997v-14.0400009 + l9-5.0400009V-16.3458252z"/> + </g> + <g id="Semibold-S" transform="matrix(1 0 0 1 1994.05 696)"> + <path d="M51.4601669-73.1324997c3.4799995-1.9200058,6.7200012-1.9200058,10.0800018,0l26.7599945,15l-10.4399948,5.8800011 + L46.0601654-70.1324997L51.4601669-73.1324997z M50.9801674,3.7874999L21.7001667-12.7725 + c-2.2799988-1.3199997-3.2399998-2.7600012-3.2399998-5.1600008V-49.732502l32.5200005,18.2400017V3.7874999z + M56.5001678-40.4925003L24.8201675-58.2524986l10.7999992-6.1200066l31.6800041,17.760006L56.5001678-40.4925003z + M93.8201675-18.0524998c0,2.4000006-0.9599991,3.7199984-3.2400055,5.1599998L61.3001671,3.6675v-35.0400009l10.920002-6.1199989 + v8.7599983c0,2.7600021,1.9199982,5.1600018,4.5599976,5.5200005c3.4800034,0.6000004,6.4800034-2.0399971,6.4800034-5.3999996 + v-14.9999981l10.5599976-6V-18.0524998z"/> + </g> + <g id="Medium-S" transform="matrix(1 0 0 1 1697.56 696)"> + <path d="M45.5849686-73.7324982c3.7199974-2.0400009,7.079998-2.1600037,10.6799965,0l27.4799995,15.5999985 + l-11.3999939,6.4799995L39.4649658-70.2525024L45.5849686-73.7324982z M45.9449692,4.7474999L15.8249674-12.5325012 + c-2.3999996-1.4399986-3.3599987-2.8799992-3.3599987-5.3999996v-32.6399994l33.4799995,19.0800018V4.7474999z + M50.9849701-39.6525002L18.224968-58.1324997l11.7600021-6.7200012l32.7599983,18.6000023L50.9849701-39.6525002z + M88.9049683-17.9325008c0,2.3999996-0.9599991,3.960001-3.3600006,5.3999996L55.4249687,4.6274996v-35.8799973 + l11.8800011-6.720005v10.0800037c0,2.2799988,1.5599976,4.4400005,3.8399963,4.9200001 + c3.2399979,0.7200012,6.1200027-1.6800003,6.1200027-4.7999992v-15.7200012l11.7600021-6.7199974L88.9049683-17.9325008 + L88.9049683-17.9325008z"/> + </g> + <g id="Regular-S" transform="matrix(1 0 0 1 1401.19 696)"> + <path d="M45.7615852-74.452507c3.9600029-2.2799911,7.5600014-2.3999939,11.5201225,0l28.4398727,16.4400101 + l-12.7198792,7.3199959L38.6815872-70.2525024L45.7615852-74.452507z M47.0815887,5.9474998L15.7615871-12.0524988 + c-2.5200005-1.5600014-3.4799995-3.1200008-3.4799995-5.6400023v-33.7199974l34.8000031,20.1599998V5.9474998z + M51.5215874-38.5725021L17.4415855-58.0124969l12.9601212-7.5600052l34.079998,19.6800003L51.5215874-38.5725021z + M90.2817078-17.8125c0,2.6400003-1.0800018,4.079999-3.4800034,5.6400003l-31.3199997,18v-37.079998l13.2000046-7.6800041 + v11.7600021c0,1.8000011,1.0800018,3.6000004,2.8799973,4.0799999c2.8800049,0.9599991,5.5199966-1.1999989,5.5199966-3.9599991 + V-43.732502l13.2000046-7.6799965V-17.8125z"/> + </g> + <g id="Light-S" transform="matrix(1 0 0 1 1104.93 696)"> + <path d="M46.6980095-75.7724991c3.2399979-1.9199982,6.2399979-1.9199982,9.4799995,0l31.8000031,17.9999962 + l-14.7600098,8.2800026L36.4980087-70.0124969L46.6980095-75.7724991z M48.1380081,7.5074997L14.8980083-11.2125006 + c-2.7599993-1.6799994-3.6000004-3.1200008-3.6000004-5.6399984v-35.6399994L48.1380081-31.732502V7.5074997z + M51.9780083-37.8525009L15.1380091-58.732502l15.8399992-8.0399971l36.7199974,20.5200005L51.9780083-37.8525009z + M91.3380051-16.852499c0,2.6399984-0.8399963,3.9599991-3.5999985,5.6399984L54.4980087,7.3874998V-31.732502 + l15.2399979-8.5199966v13.9199982c0,1.4400005,0.9599991,2.7600002,2.2799988,3.1199989 + c2.1600037,0.6000004,4.0800018-0.9599991,4.0800018-3v-17.6399994l15.2399979-8.6399994V-16.852499z"/> + </g> + <g id="Thin-S" transform="matrix(1 0 0 1 808.842 696)"> + <path d="M47.4701958-77.5725021c2.279995-1.3199997,4.5599976-1.3199997,6.8400002,0l36.3600006,20.1600037L73.270195-47.9325027 + L33.3101959-69.7724991L47.4701958-77.5725021z M49.270195,9.5474997L13.390193-10.0125008 + c-3.1199999-1.6799994-3.5999994-3-3.5999994-5.6399984v-38.2800026l39.4800034,21.720005V9.5474997z M12.9101934-57.8925018 + l16.2000008-9l39.1200027,21.1199989l-16.7999992,9L12.9101934-57.8925018z M92.470192-15.6524992 + c0,2.6399984-0.5999985,3.9599991-3.5999985,5.6399984L52.9901924,9.5474997v-41.6399994l17.8800011-9.840004v16.920002 + c0,0.960001,0.7200012,1.6800003,1.5599976,1.7999992c1.1999969,0.2400017,2.1600037-0.7199993,2.1600037-1.7999992v-18.9600029 + L92.470192-53.8125V-15.6524992z"/> + </g> + <g id="Ultralight-S" transform="matrix(1 0 0 1 512.461 696)"> + <path d="M45.3409958-78.5324936c1.7999992-1.0800018,3.7200012-1.0800018,5.3999977,0l38.6400032,21.239994L70.6609955-47.0924988 + L29.0209961-69.652504L45.3409958-78.5324936z M47.140995,10.6274996L10.0609961-9.4125004 + c-3.2399993-1.8000002-3.7199988-3-3.7199988-5.6399984v-39.720005L47.140995-32.5725021V10.6274996z M6.7009964-57.2924995 + l19.6800003-10.7999992L67.901001-45.6525002L47.9809952-34.8525009L6.7009964-57.2924995z M90.3409958-15.0524988 + c0,2.6399984-0.4800034,3.8399982-3.7200012,5.6399984l-37.079998,19.9200001v-43.0800018l19.2000008-10.4399948v18.3599968 + c0,0.7199993,0.5999985,1.0799999,1.1999969,1.2000008c0.7200012,0,1.1999969-0.4800014,1.1999969-1.2000008v-19.6800003 + l19.2000046-10.4400024V-15.0524988z"/> + </g> +</g> +</svg> diff --git a/MacLibrary/Icons/AppIcon.icns b/MacLibrary/Icons/AppIcon.icns new file mode 100644 index 0000000000000000000000000000000000000000..c1b8ed06305c975366c8398f4d75402a9d319ddc Binary files /dev/null and b/MacLibrary/Icons/AppIcon.icns differ diff --git a/MacLibrary/MacApplication.swift b/MacLibrary/MacApplication.swift new file mode 100644 index 0000000000000000000000000000000000000000..fe71828a5e6605b03ddbc70be4b67496d273a5aa --- /dev/null +++ b/MacLibrary/MacApplication.swift @@ -0,0 +1,83 @@ +import ApplicationLibrary +import Library +import SwiftUI + +public struct MacApplication: Scene { + @State private var showMenuBarExtra = false + @State private var isMenuPresented = false + @StateObject private var environments = ExtensionEnvironments() + + public init() {} + public var body: some Scene { + Window("sing-box", id: "main", content: { + MainView() + .onAppear { + Task { + await initialize() + } + } + .environment(\.showMenuBarExtra, $showMenuBarExtra) + .environmentObject(environments) + }) + .commands { + if showMenuBarExtra { + CommandGroup(replacing: .appTermination) { + Button("Quit sing-box") { + hide(closeApp: true) + } + .keyboardShortcut("q", modifiers: [.command]) + } + CommandGroup(replacing: .saveItem) { + Button("Close") { + hide(closeApp: false) + } + .keyboardShortcut("w", modifiers: [.command]) + } + } + SidebarCommands() + CommandGroup(replacing: .appSettings) { + Button("Settings") { + environments.openSettings.send() + } + .keyboardShortcut(",", modifiers: [.command]) + } + } + + MenuBarExtra(isInserted: $showMenuBarExtra) { + MenuView(isMenuPresented: $isMenuPresented) + .environmentObject(environments) + } label: { + Image("MenuIcon") + } + .menuBarExtraStyle(.window) + .menuBarExtraAccess(isPresented: $isMenuPresented) + } + + private func initialize() async { + showMenuBarExtra = await SharedPreferences.showMenuBarExtra.get() + } + + private func hide(closeApp: Bool) { + Task { + if await SharedPreferences.menuBarExtraInBackground.get() { + hide0(closeApp: closeApp) + } else { + if closeApp { + NSApp.terminate(nil) + } else { + NSApp.keyWindow?.close() + } + } + } + } + + private func hide0(closeApp: Bool) { + if closeApp || NSApp.keyWindow?.identifier?.rawValue == "main" { + let transformState = ProcessApplicationTransformState(kProcessTransformToUIElementApplication) + var psn = ProcessSerialNumber(highLongOfPSN: 0, lowLongOfPSN: UInt32(kCurrentProcess)) + TransformProcessType(&psn, transformState) + NSApp.setActivationPolicy(.accessory) + } + NSApp.keyWindow?.close() + } +} diff --git a/MacLibrary/MacLibrary.swift b/MacLibrary/MacLibrary.swift new file mode 100644 index 0000000000000000000000000000000000000000..47221be34b8b6edf8d4efc534188aa8064307b89 --- /dev/null +++ b/MacLibrary/MacLibrary.swift @@ -0,0 +1,3 @@ +import Foundation + +public class MacLibrary {} diff --git a/MacLibrary/MainView.swift b/MacLibrary/MainView.swift new file mode 100644 index 0000000000000000000000000000000000000000..79379547413ac29c45323b02d1ad1947f194866a --- /dev/null +++ b/MacLibrary/MainView.swift @@ -0,0 +1,121 @@ +import ApplicationLibrary +import Libbox +import Library +import SwiftUI + +@MainActor +public struct MainView: View { + @Environment(\.controlActiveState) private var controlActiveState + @EnvironmentObject private var environments: ExtensionEnvironments + + @State private var selection = NavigationPage.dashboard + @State private var importProfile: LibboxProfileContent? + @State private var importRemoteProfile: LibboxImportRemoteProfile? + @State private var alert: Alert? + + public init() {} + public var body: some View { + if ApplicationLibrary.inPreview { + body1.frame(width: 1280, height: 750, alignment: .topLeading) + } else { + body1 + } + } + + private var body1: some View { + NavigationSplitView { + SidebarView() + } detail: { + NavigationStack { + selection.contentView + .navigationTitle(selection.title) + } + } + .onAppear { + environments.postReload() + #if !DEBUG + if Variant.useSystemExtension { + Task { + checkApplicationPath() + } + } + #endif + } + .alertBinding($alert) + .toolbar { + ToolbarItem(placement: .navigation) { + StartStopButton() + } + } + .onChangeCompat(of: controlActiveState) { newValue in + if newValue != .inactive { + environments.postReload() + } + } + .onChangeCompat(of: selection) { value in + if value == .logs { + environments.connectLog() + } + } + .onReceive(environments.openSettings) { + selection = .settings + } + .formStyle(.grouped) + .environment(\.selection, $selection) + .environment(\.importProfile, $importProfile) + .environment(\.importRemoteProfile, $importRemoteProfile) + .handlesExternalEvents(preferring: [], allowing: ["*"]) + .onOpenURL(perform: openURL) + } + + private func openURL(url: URL) { + if url.host == "import-remote-profile" { + var error: NSError? + importRemoteProfile = LibboxParseRemoteProfileImportLink(url.absoluteString, &error) + if error != nil { + return + } + if selection != .profiles { + selection = .profiles + } + } else if url.pathExtension == "bpf" { + Task { + await importURLProfile(url) + } + } else { + alert = Alert(errorMessage: "Handled unknown URL \(url.absoluteString)") + } + } + + private func importURLProfile(_ url: URL) async { + do { + _ = url.startAccessingSecurityScopedResource() + importProfile = try await .from(readURL(url)) + url.stopAccessingSecurityScopedResource() + } catch { + alert = Alert(error) + return + } + if selection != .profiles { + selection = .profiles + } + } + + private nonisolated func readURL(_ url: URL) async throws -> Data { + try Data(contentsOf: url) + } + + private func checkApplicationPath() { + let directoryName = URL(filePath: Bundle.main.bundlePath).deletingLastPathComponent().pathComponents.last + if directoryName != "Applications" { + alert = Alert( + title: Text("Wrong application location"), + message: Text("This app needs to be placed under ~/Applications to work."), + dismissButton: .default(Text("Ok")) { + NSWorkspace.shared.selectFile(Bundle.main.bundlePath, inFileViewerRootedAtPath: "") + NSApp.terminate(nil) + } + ) + } + } +} diff --git a/MacLibrary/MenuLabel.swift b/MacLibrary/MenuLabel.swift new file mode 100644 index 0000000000000000000000000000000000000000..6275f5b66a9e8b416ecefee92c0b1417b8d68c14 --- /dev/null +++ b/MacLibrary/MenuLabel.swift @@ -0,0 +1,40 @@ +import Libbox +import Library +import SwiftUI + +public struct MenuLabel: View { + @EnvironmentObject private var environments: ExtensionEnvirnments + + public init() {} + public var body: some View { +// if let profile = environments.extensionProfile { +// MenuLabel0().environmentObject(profile) +// } else { +// } + } + + private struct MenuLabel0: View { + @EnvironmentObject private var environments: ExtensionEnvirnments + @EnvironmentObject private var extensionProfile: ExtensionProfile + @StateObject private var commandClient = CommandClient(.status) + + var body: some View { + HStack { + if extensionProfile.status.isConnectedStrict, let message = commandClient.status { + Image("MenuIcon") + Text(" ↑ \(LibboxFormatBytes(message.uplink))/s ↓ \(LibboxFormatBytes(message.downlink))/s") + } else { + Image("MenuIcon") + } + } + .onAppear { + commandClient.connect() + } + .onChangeCompat(of: extensionProfile.status) { newValue in + if newValue.isConnectedStrict { + commandClient.connect() + } + } + } + } +} diff --git a/MacLibrary/MenuView.swift b/MacLibrary/MenuView.swift new file mode 100644 index 0000000000000000000000000000000000000000..ea9764dfb01af294e6653fd7533dea48d9a3044b --- /dev/null +++ b/MacLibrary/MenuView.swift @@ -0,0 +1,207 @@ +import ApplicationLibrary +import Foundation +import Libbox +import Library +import MacControlCenterUI +import MenuBarExtraAccess +import SwiftUI + +@MainActor +public struct MenuView: View { + @Environment(\.openWindow) private var openWindow + + private static let sliderWidth: CGFloat = 270 + + @Binding private var isMenuPresented: Bool + + @State private var isLoading = true + @State private var profile: ExtensionProfile? + + public init(isMenuPresented: Binding<Bool>) { + _isMenuPresented = isMenuPresented + } + + public var body: some View { + MacControlCenterMenu(isPresented: $isMenuPresented) { + MenuHeader("sing-box") { + if isLoading { + Text("Loading...").foregroundColor(.secondary).onAppear { + Task { + await loadProfile() + } + } + } else if let profile { + Text(LibboxVersion()).foregroundColor(.secondary) + StatusSwitch(profile) + } else { + Text("NetworkExtension not installed") + } + } + .frame(minWidth: MenuView.sliderWidth) + if let profile { + ProfilePicker(profile) + } + Divider() + MenuCommand { + NSApp.setActivationPolicy(.regular) + openWindow(id: "main") + if let dockApp = NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.dock").first { + dockApp.activate() + DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) { + NSApp.activate(ignoringOtherApps: true) + } + } + } label: { + Text("Open") + } + MenuCommand { + NSApp.terminate(nil) + } label: { + Text("Quit") + } + } + } + + private func loadProfile() async { + profile = try? await ExtensionProfile.load() + if let profile { + profile.register() + } + isLoading = false + } + + private struct StatusSwitch: View { + @ObservedObject private var profile: ExtensionProfile + @State private var alert: Alert? + + init(_ profile: ExtensionProfile) { + self.profile = profile + } + + var body: some View { + Toggle(isOn: Binding(get: { + profile.status.isConnected + }, set: { _ in + Task { + await switchProfile(!profile.status.isConnected) + } + })) {} + .toggleStyle(.switch) + .disabled(!profile.status.isEnabled) + .alertBinding($alert) + } + + private func switchProfile(_ isEnabled: Bool) async { + do { + if isEnabled { + try await profile.start() + } else { + try await profile.stop() + } + } catch { + alert = Alert(error) + return + } + } + } + + private struct ProfilePicker: View { + @EnvironmentObject private var environments: ExtensionEnvironments + @ObservedObject private var profile: ExtensionProfile + + init(_ profile: ExtensionProfile) { + self.profile = profile + } + + @State private var isLoading = true + @State private var profileList: [ProfilePreview] = [] + @State private var selectedProfileID: Int64 = 0 + @State private var reasserting = false + @State private var alert: Alert? + + private var selectedProfileIDLocal: Binding<Int64> { + $selectedProfileID.withSetter { newValue in + reasserting = true + Task { [self] in + await switchProfile(newValue) + } + } + } + + var body: some View { + viewBuilder { + if isLoading { + ProgressView().onAppear { + Task { + await doReload() + } + } + } else { + if profileList.isEmpty { + Text("Empty profiles") + } else { + MenuSection("Profile") + Picker("", selection: selectedProfileIDLocal) { + ForEach(profileList, id: \.id) { profile in + Text(profile.name) + } + } + .pickerStyle(.inline) + .disabled(!profile.status.isSwitchable || reasserting) + } + } + } + .onReceive(environments.profileUpdate) { _ in + Task { + await doReload() + } + } + .onReceive(environments.selectedProfileUpdate) { _ in + Task { + selectedProfileID = await SharedPreferences.selectedProfileID.get() + } + } + .alertBinding($alert) + } + + private func doReload() async { + defer { + isLoading = false + } + do { + profileList = try await ProfileManager.list().map { ProfilePreview($0) } + } catch { + alert = Alert(error) + return + } + if profileList.isEmpty { + return + } + selectedProfileID = await SharedPreferences.selectedProfileID.get() + if profileList.filter({ profile in + profile.id == selectedProfileID + }) + .isEmpty { + selectedProfileID = profileList[0].id + await SharedPreferences.selectedProfileID.set(selectedProfileID) + } + } + + private func switchProfile(_ newProfileID: Int64) async { + await SharedPreferences.selectedProfileID.set(newProfileID) + environments.selectedProfileUpdate.send() + if profile.status.isConnected { + do { + try await serviceReload() + } catch { + alert = Alert(error) + } + } + reasserting = false + } + + private nonisolated func serviceReload() async throws { + try LibboxNewStandaloneCommandClient()!.serviceReload() + } + } +} diff --git a/MacLibrary/SidebarView.swift b/MacLibrary/SidebarView.swift new file mode 100644 index 0000000000000000000000000000000000000000..df83edf1b74a5da9629c7f59dd2d868a2d12a3db --- /dev/null +++ b/MacLibrary/SidebarView.swift @@ -0,0 +1,72 @@ +import ApplicationLibrary +import Library +import SwiftUI + +public struct SidebarView: View { + @Environment(\.selection) private var selection + @EnvironmentObject private var environments: ExtensionEnvironments + + public init() {} + public var body: some View { + VStack { + if environments.extensionProfileLoading { + ProgressView() + } else if let profile = environments.extensionProfile { + SidebarView0().environmentObject(profile) + } else { + SidebarView1() + } + }.frame(minWidth: 150) + } + + struct SidebarView0: View { + @Environment(\.selection) private var selection + @EnvironmentObject private var extensionProfile: ExtensionProfile + + var body: some View { + VStack { + viewBuilder { + if extensionProfile.status.isConnectedStrict { + List(selection: selection) { + Section(NavigationPage.dashboard.title) { + Label("Overview", systemImage: "text.and.command.macwindow") + .tint(.textColor) + .tag(NavigationPage.dashboard) + NavigationPage.groups.label.tag(NavigationPage.groups) + } + Divider() + ForEach(NavigationPage.macosDefaultPages, id: \.self) { it in + it.label + } + } + } else { + List(NavigationPage.allCases.filter { it in + it.visible(extensionProfile) + }, selection: selection) { it in + it.label + } + } + } + .listStyle(.sidebar) + .scrollDisabled(true) + } + .onChangeCompat(of: extensionProfile.status) { + if !selection.wrappedValue.visible(extensionProfile) { + selection.wrappedValue = NavigationPage.dashboard + } + } + } + } + + struct SidebarView1: View { + @Environment(\.selection) private var selection + + var body: some View { + List(NavigationPage.allCases.filter { it in + it.visible(nil) + }, selection: selection) { it in + it.label + } + } + } +} diff --git a/README.md b/README.md index fa4c3bd86892b3d64b5e0a84f896e89ba099f719..eaae019abc67c13616cb0a966a6eb90c5d1b1ab1 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,26 @@ -# Sing-box +# sing-box-for-apple +Experimental iOS/macOS/tvOS client for sing-box, the universal proxy platform. +## Documentation -## Getting started +[SFI](https://sing-box.sagernet.org/installation/clients/sfi/) | [SFM](https://sing-box.sagernet.org/installation/clients/sfm/) -To make it easy for you to get started with GitLab, here's a list of recommended next steps. - -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! - -## Add your files - -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: +## License ``` -cd existing_repo -git remote add origin https://git.beinmedia.com/George/sing-box.git -git branch -M main -git push -uf origin main -``` - -## Integrate with your tools - -- [ ] [Set up project integrations](https://git.beinmedia.com/George/sing-box/-/settings/integrations) - -## Collaborate with your team - -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) - -## Test and Deploy - -Use the built-in continuous integration in GitLab. - -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) - -*** - -# Editing this README - -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template. +Copyright (C) 2022 by nekohasekai <contact-sagernet@sekai.icu> -## Suggestions for a good README +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. - -## Name -Choose a self-explaining name for your project. - -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. - -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. - -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. - -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. - -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. - -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. - -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. - -## Contributing -State if you are open to contributions and what your requirements are for accepting them. - -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. - -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. - -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. - -## License -For open source projects, say how it is licensed. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +``` \ No newline at end of file diff --git a/SFI/Application.swift b/SFI/Application.swift new file mode 100644 index 0000000000000000000000000000000000000000..ec6c6add6ea060f2578ba44350c1634373606545 --- /dev/null +++ b/SFI/Application.swift @@ -0,0 +1,16 @@ +import Foundation +import Library +import SwiftUI + +@main +struct Application: App { + @UIApplicationDelegateAdaptor private var appDelegate: ApplicationDelegate + @StateObject private var environments = ExtensionEnvironments() + + var body: some Scene { + WindowGroup { + MainView() + .environmentObject(environments) + } + } +} diff --git a/SFI/ApplicationDelegate.swift b/SFI/ApplicationDelegate.swift new file mode 100644 index 0000000000000000000000000000000000000000..01202425e003685e649d14abbd2a05a84672d7e3 --- /dev/null +++ b/SFI/ApplicationDelegate.swift @@ -0,0 +1,66 @@ +import ApplicationLibrary +import Foundation +import Libbox +import Library +import Network +import UIKit + +class ApplicationDelegate: NSObject, UIApplicationDelegate { + private var profileServer: ProfileServer? + + func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { + NSLog("Here I stand") + LibboxSetup(FilePath.sharedDirectory.relativePath, FilePath.workingDirectory.relativePath, FilePath.cacheDirectory.relativePath, false) + setup() + return true + } + + private func setup() { + do { + try UIProfileUpdateTask.configure() + NSLog("setup background task success") + } catch { + NSLog("setup background task error: \(error.localizedDescription)") + } + Task { + if UIDevice.current.userInterfaceIdiom == .phone { + await requestNetworkPermission() + } + await setupBackground() + } + } + + private nonisolated func setupBackground() async { + if #available(iOS 16.0, *) { + do { + let profileServer = try ProfileServer() + profileServer.start() + await MainActor.run { + self.profileServer = profileServer + } + NSLog("started profile server") + } catch { + NSLog("setup profile server error: \(error.localizedDescription)") + } + } + } + + private nonisolated func requestNetworkPermission() async { + if await SharedPreferences.networkPermissionRequested.get() { + return + } + if !DeviceCensorship.isChinaDevice() { + await SharedPreferences.networkPermissionRequested.set(true) + return + } + URLSession.shared.dataTask(with: URL(string: "http://captive.apple.com")!) { _, response, _ in + if let response = response as? HTTPURLResponse { + if response.statusCode == 200 { + Task { + await SharedPreferences.networkPermissionRequested.set(true) + } + } + } + }.resume() + } +} diff --git a/SFI/Assets.xcassets/AccentColor.colorset/Contents.json b/SFI/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..0afb3cf0eec828abf43c44d0b7a43d32345cfc80 --- /dev/null +++ b/SFI/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors": [ + { + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/SFI/Assets.xcassets/AppIcon.appiconset/100.png b/SFI/Assets.xcassets/AppIcon.appiconset/100.png new file mode 100644 index 0000000000000000000000000000000000000000..87b49a299bbe97dad7baf0d705c37bcff4f099a0 Binary files /dev/null and b/SFI/Assets.xcassets/AppIcon.appiconset/100.png differ diff --git a/SFI/Assets.xcassets/AppIcon.appiconset/1024.png b/SFI/Assets.xcassets/AppIcon.appiconset/1024.png new file mode 100644 index 0000000000000000000000000000000000000000..c85573af6199956760318296bf622679a7ddf708 Binary files /dev/null and b/SFI/Assets.xcassets/AppIcon.appiconset/1024.png differ diff --git a/SFI/Assets.xcassets/AppIcon.appiconset/114.png b/SFI/Assets.xcassets/AppIcon.appiconset/114.png new file mode 100644 index 0000000000000000000000000000000000000000..a89cecfeff8831c537fa9beda1227839115aae85 Binary files /dev/null and b/SFI/Assets.xcassets/AppIcon.appiconset/114.png differ diff --git a/SFI/Assets.xcassets/AppIcon.appiconset/120.png b/SFI/Assets.xcassets/AppIcon.appiconset/120.png new file mode 100644 index 0000000000000000000000000000000000000000..08eb7d98698a6832d989d56d28613250cebfd469 Binary files /dev/null and b/SFI/Assets.xcassets/AppIcon.appiconset/120.png differ diff --git a/SFI/Assets.xcassets/AppIcon.appiconset/144.png b/SFI/Assets.xcassets/AppIcon.appiconset/144.png new file mode 100644 index 0000000000000000000000000000000000000000..48a3e7512deb2bc81c2f4b018973681bc03b4601 Binary files /dev/null and b/SFI/Assets.xcassets/AppIcon.appiconset/144.png differ diff --git a/SFI/Assets.xcassets/AppIcon.appiconset/152.png b/SFI/Assets.xcassets/AppIcon.appiconset/152.png new file mode 100644 index 0000000000000000000000000000000000000000..00a9960b16675c17abf87363f8400cdabaf25f2e Binary files /dev/null and b/SFI/Assets.xcassets/AppIcon.appiconset/152.png differ diff --git a/SFI/Assets.xcassets/AppIcon.appiconset/167.png b/SFI/Assets.xcassets/AppIcon.appiconset/167.png new file mode 100644 index 0000000000000000000000000000000000000000..713f83f36dcd50598554dfdbea5b31694bfc20e6 Binary files /dev/null and b/SFI/Assets.xcassets/AppIcon.appiconset/167.png differ diff --git a/SFI/Assets.xcassets/AppIcon.appiconset/180.png b/SFI/Assets.xcassets/AppIcon.appiconset/180.png new file mode 100644 index 0000000000000000000000000000000000000000..42819bb4717dc2a3bf4514934cbc134dd77843d6 Binary files /dev/null and b/SFI/Assets.xcassets/AppIcon.appiconset/180.png differ diff --git a/SFI/Assets.xcassets/AppIcon.appiconset/20.png b/SFI/Assets.xcassets/AppIcon.appiconset/20.png new file mode 100644 index 0000000000000000000000000000000000000000..eb62ba33716c3dd3a6b5c9ca123e3c235852127e Binary files /dev/null and b/SFI/Assets.xcassets/AppIcon.appiconset/20.png differ diff --git a/SFI/Assets.xcassets/AppIcon.appiconset/29.png b/SFI/Assets.xcassets/AppIcon.appiconset/29.png new file mode 100644 index 0000000000000000000000000000000000000000..44d1f93c9283ae7fb02e9972b017523506af2f95 Binary files /dev/null and b/SFI/Assets.xcassets/AppIcon.appiconset/29.png differ diff --git a/SFI/Assets.xcassets/AppIcon.appiconset/40.png b/SFI/Assets.xcassets/AppIcon.appiconset/40.png new file mode 100644 index 0000000000000000000000000000000000000000..9f2320e9c9284095be8d6368fa880d979ff57e23 Binary files /dev/null and b/SFI/Assets.xcassets/AppIcon.appiconset/40.png differ diff --git a/SFI/Assets.xcassets/AppIcon.appiconset/50.png b/SFI/Assets.xcassets/AppIcon.appiconset/50.png new file mode 100644 index 0000000000000000000000000000000000000000..f6871cedbf7b97ff5651cbddf9c4092aa7a45f33 Binary files /dev/null and b/SFI/Assets.xcassets/AppIcon.appiconset/50.png differ diff --git a/SFI/Assets.xcassets/AppIcon.appiconset/57.png b/SFI/Assets.xcassets/AppIcon.appiconset/57.png new file mode 100644 index 0000000000000000000000000000000000000000..7942d4681b3824c79ef6ff682c686fcc0b37296b Binary files /dev/null and b/SFI/Assets.xcassets/AppIcon.appiconset/57.png differ diff --git a/SFI/Assets.xcassets/AppIcon.appiconset/58.png b/SFI/Assets.xcassets/AppIcon.appiconset/58.png new file mode 100644 index 0000000000000000000000000000000000000000..8e5cb599587fe9587eb4d1087db44d3673b5a1d3 Binary files /dev/null and b/SFI/Assets.xcassets/AppIcon.appiconset/58.png differ diff --git a/SFI/Assets.xcassets/AppIcon.appiconset/60.png b/SFI/Assets.xcassets/AppIcon.appiconset/60.png new file mode 100644 index 0000000000000000000000000000000000000000..5951cae25cd1ee91e3e789c47ae4d5f83507a43b Binary files /dev/null and b/SFI/Assets.xcassets/AppIcon.appiconset/60.png differ diff --git a/SFI/Assets.xcassets/AppIcon.appiconset/72.png b/SFI/Assets.xcassets/AppIcon.appiconset/72.png new file mode 100644 index 0000000000000000000000000000000000000000..6b405eba5d38e26c3b74aa43bc0966ebd5b319b0 Binary files /dev/null and b/SFI/Assets.xcassets/AppIcon.appiconset/72.png differ diff --git a/SFI/Assets.xcassets/AppIcon.appiconset/76.png b/SFI/Assets.xcassets/AppIcon.appiconset/76.png new file mode 100644 index 0000000000000000000000000000000000000000..9166aa451d9e6da0e67db06af23b8b57bd7e5a20 Binary files /dev/null and b/SFI/Assets.xcassets/AppIcon.appiconset/76.png differ diff --git a/SFI/Assets.xcassets/AppIcon.appiconset/80.png b/SFI/Assets.xcassets/AppIcon.appiconset/80.png new file mode 100644 index 0000000000000000000000000000000000000000..f4c0c93ed79b69d5472936ff6e6af7414e907591 Binary files /dev/null and b/SFI/Assets.xcassets/AppIcon.appiconset/80.png differ diff --git a/SFI/Assets.xcassets/AppIcon.appiconset/87.png b/SFI/Assets.xcassets/AppIcon.appiconset/87.png new file mode 100644 index 0000000000000000000000000000000000000000..3c1f73d5ff9d99f725e0b146f86a99234287904d Binary files /dev/null and b/SFI/Assets.xcassets/AppIcon.appiconset/87.png differ diff --git a/SFI/Assets.xcassets/AppIcon.appiconset/Contents.json b/SFI/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..799689a51b2c65c1f6aa891e2bd74c059f144b6f --- /dev/null +++ b/SFI/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,324 @@ +{ + "images" : [ + { + "filename" : "40.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "60.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "filename" : "29.png", + "idiom" : "iphone", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "58.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "87.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "filename" : "80.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "120.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "filename" : "57.png", + "idiom" : "iphone", + "scale" : "1x", + "size" : "57x57" + }, + { + "filename" : "114.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "57x57" + }, + { + "filename" : "120.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "180.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "filename" : "20.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "filename" : "40.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "29.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "58.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "40.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "filename" : "80.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "50.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "50x50" + }, + { + "filename" : "100.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "50x50" + }, + { + "filename" : "72.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "72x72" + }, + { + "filename" : "144.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "72x72" + }, + { + "filename" : "76.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "filename" : "152.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "filename" : "167.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "filename" : "1024.png", + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + }, + { + "idiom" : "watch", + "role" : "notificationCenter", + "scale" : "2x", + "size" : "24x24", + "subtype" : "38mm" + }, + { + "idiom" : "watch", + "role" : "notificationCenter", + "scale" : "2x", + "size" : "27.5x27.5", + "subtype" : "42mm" + }, + { + "idiom" : "watch", + "role" : "companionSettings", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "watch", + "role" : "companionSettings", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "watch", + "role" : "notificationCenter", + "scale" : "2x", + "size" : "33x33", + "subtype" : "45mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "40x40", + "subtype" : "38mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "44x44", + "subtype" : "40mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "46x46", + "subtype" : "41mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "50x50", + "subtype" : "44mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "51x51", + "subtype" : "45mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "54x54", + "subtype" : "49mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "86x86", + "subtype" : "38mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "98x98", + "subtype" : "42mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "108x108", + "subtype" : "44mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "117x117", + "subtype" : "45mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "129x129", + "subtype" : "49mm" + }, + { + "filename" : "1024.png", + "idiom" : "watch-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SFI/Assets.xcassets/Contents.json b/SFI/Assets.xcassets/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..74d6a722cf39b2dd5bef11f166e6ac9b99568a01 --- /dev/null +++ b/SFI/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/SFI/ContentView.swift b/SFI/ContentView.swift new file mode 100644 index 0000000000000000000000000000000000000000..dcd8a312513d4a926bcbf1f437814574b3bf632f --- /dev/null +++ b/SFI/ContentView.swift @@ -0,0 +1,59 @@ +import ApplicationLibrary +import Library +import SwiftUI + +struct ContentView: View { + @Environment(\.selection) private var selection + @Environment(\.extensionProfile) private var extensionProfile + + var body: some View { + viewBuilder { + if let profile = extensionProfile.wrappedValue { + ContentView0().environmentObject(profile) + } else { + ContentView1() + } + } + } + + struct ContentView0: View { + @Environment(\.selection) private var selection + @EnvironmentObject private var extensionProfile: ExtensionProfile + + var body: some View { + TabView(selection: selection) { + ForEach(NavigationPage.allCases.filter { it in + it.visible(extensionProfile) + }, id: \.self) { page in + NavigationStackCompat { + page.contentView + } + .tag(page) + .tabItem { page.label } + } + }.onChangeCompat(of: extensionProfile.status) { + if !selection.wrappedValue.visible(extensionProfile) { + selection.wrappedValue = NavigationPage.dashboard + } + } + } + } + + struct ContentView1: View { + @Environment(\.selection) private var selection + + var body: some View { + TabView(selection: selection) { + ForEach(NavigationPage.allCases.filter { it in + it.visible(nil) + }, id: \.self) { page in + NavigationStackCompat { + page.contentView + } + .tag(page) + .tabItem { page.label } + } + } + } + } +} diff --git a/SFI/Info.plist b/SFI/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..4063066bf3d3635d07c71bac5dcd74153753c977 --- /dev/null +++ b/SFI/Info.plist @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>BGTaskSchedulerPermittedIdentifiers</key> + <array> + <string>io.nekohasekai.sfabeino.update_profiles</string> + </array> + <key>CFBundleDocumentTypes</key> + <array> + <dict> + <key>CFBundleTypeIconFile</key> + <string>AppIcon.icns</string> + <key>CFBundleTypeName</key> + <string>sing-box Profile</string> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + <key>LSHandlerRank</key> + <string>Owner</string> + <key>LSItemContentTypes</key> + <array> + <string>io.nekohasekai.sfabeino.profile</string> + </array> + </dict> + </array> + <key>CFBundleURLTypes</key> + <array> + <dict> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + <key>CFBundleURLIconFile</key> + <string>AppIcon.icns</string> + <key>CFBundleURLName</key> + <string>sing-box</string> + <key>CFBundleURLSchemes</key> + <array> + <string>sing-box</string> + </array> + </dict> + </array> + <key>ITSAppUsesNonExemptEncryption</key> + <false/> + <key>NSApplicationServices</key> + <dict> + <key>Advertises</key> + <array> + <dict> + <key>NSApplicationServiceIdentifier</key> + <string>sing-box:profile</string> + </dict> + </array> + </dict> + <key>NSUbiquitousContainers</key> + <dict> + <key>iCloud.io.nekohasekai.sfabeino</key> + <dict> + <key>NSUbiquitousContainerIsDocumentScopePublic</key> + <true/> + <key>NSUbiquitousContainerName</key> + <string>sing-box</string> + <key>NSUbiquitousContainerSupportedFolderLevels</key> + <string>Any</string> + </dict> + </dict> + <key>UIBackgroundModes</key> + <array> + <string>fetch</string> + </array> + <key>UTExportedTypeDeclarations</key> + <array> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.item</string> + <string>public.content</string> + <string>public.data</string> + </array> + <key>UTTypeDescription</key> + <string>sing-box Profile</string> + <key>UTTypeIconFiles</key> + <array/> + <key>UTTypeIdentifier</key> + <string>io.nekohasekai.sfabeino.profile</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>bpf</string> + </array> + </dict> + </dict> + </array> + <key>UTImportedTypeDeclarations</key> + <array> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.item</string> + <string>public.content</string> + <string>public.data</string> + </array> + <key>UTTypeDescription</key> + <string>sing-box Profile</string> + <key>UTTypeIconFiles</key> + <array> + <string>AppIcon.icns</string> + </array> + <key>UTTypeIdentifier</key> + <string>io.nekohasekai.sfabeino.profile</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>bpf</string> + </array> + </dict> + </dict> + </array> +</dict> +</plist> diff --git a/SFI/MainView.swift b/SFI/MainView.swift new file mode 100644 index 0000000000000000000000000000000000000000..b854a866bb3219c3a16181327c21356059ead135 --- /dev/null +++ b/SFI/MainView.swift @@ -0,0 +1,82 @@ +import ApplicationLibrary +import Libbox +import Library +import SwiftUI + +struct MainView: View { + @Environment(\.scenePhase) private var scenePhase + @EnvironmentObject private var environments: ExtensionEnvironments + + @State private var selection = NavigationPage.dashboard + @State private var importProfile: LibboxProfileContent? + @State private var importRemoteProfile: LibboxImportRemoteProfile? + @State private var alert: Alert? + + var body: some View { + if ApplicationLibrary.inPreview { + body1.preferredColorScheme(.dark) + } else { + body1 + } + } + + var body1: some View { + TabView(selection: $selection) { + ForEach(NavigationPage.allCases, id: \.self) { page in + NavigationStackCompat { + page.contentView + .navigationTitle(page.title) + } + .tag(page) + .tabItem { page.label } + } + } + .onAppear { + environments.postReload() + } + .alertBinding($alert) + .onChangeCompat(of: scenePhase) { newValue in + if newValue == .active { + environments.postReload() + } + } + .onChangeCompat(of: selection) { newValue in + if newValue == .logs { + environments.connectLog() + } + } + .environment(\.selection, $selection) + .environment(\.importProfile, $importProfile) + .environment(\.importRemoteProfile, $importRemoteProfile) + .handlesExternalEvents(preferring: [], allowing: ["*"]) + .onOpenURL(perform: openURL) + } + + private func openURL(url: URL) { + if url.host == "import-remote-profile" { + var error: NSError? + importRemoteProfile = LibboxParseRemoteProfileImportLink(url.absoluteString, &error) + if let error { + alert = Alert(error) + return + } + if selection != .profiles { + selection = .profiles + } + } else if url.pathExtension == "bpf" { + do { + _ = url.startAccessingSecurityScopedResource() + importProfile = try .from(Data(contentsOf: url)) + url.stopAccessingSecurityScopedResource() + } catch { + alert = Alert(error) + return + } + if selection != .profiles { + selection = .profiles + } + } else { + alert = Alert(errorMessage: "Handled unknown URL \(url.absoluteString)") + } + } +} diff --git a/SFI/SFI.entitlements b/SFI/SFI.entitlements new file mode 100644 index 0000000000000000000000000000000000000000..c7ae77b14f375b43e5adcd134f10cd1a4578bb4b --- /dev/null +++ b/SFI/SFI.entitlements @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.developer.icloud-container-identifiers</key> + <array> + <string>iCloud.io.nekohasekai.sfabeino</string> + </array> + <key>com.apple.developer.icloud-services</key> + <array> + <string>CloudDocuments</string> + </array> + <key>com.apple.developer.networking.networkextension</key> + <array> + <string>packet-tunnel-provider</string> + </array> + <key>com.apple.developer.ubiquity-container-identifiers</key> + <array> + <string>iCloud.io.nekohasekai.sfabeino</string> + </array> + <key>com.apple.security.application-groups</key> + <array> + <string>group.io.nekohasekai.sfabeino</string> + </array> +</dict> +</plist> diff --git a/SFI/Upload.plist b/SFI/Upload.plist new file mode 100644 index 0000000000000000000000000000000000000000..6cd97c66e430d5f488669a2274951109e9c5b146 --- /dev/null +++ b/SFI/Upload.plist @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>destination</key> + <string>upload</string> + <key>method</key> + <string>app-store</string> + <key>manageAppVersionAndBuildNumber</key> + <true/> + <key>uploadSymbols</key> + <true/> +</dict> +</plist> diff --git a/SFM.System/Application.swift b/SFM.System/Application.swift new file mode 100644 index 0000000000000000000000000000000000000000..49bb5773d28f4decdd2f431d67ffb3ad2edff40f --- /dev/null +++ b/SFM.System/Application.swift @@ -0,0 +1,12 @@ +import Library +import MacLibrary +import SwiftUI + +@main +struct Application: App { + @NSApplicationDelegateAdaptor private var appDelegate: IndependentApplicationDelegate + + var body: some Scene { + MacApplication() + } +} diff --git a/SFM.System/IndependentApplicationDelegate.swift b/SFM.System/IndependentApplicationDelegate.swift new file mode 100644 index 0000000000000000000000000000000000000000..401e6bd1da669621a528f1bc237ffc418c9959ac --- /dev/null +++ b/SFM.System/IndependentApplicationDelegate.swift @@ -0,0 +1,27 @@ +import AppKit +import Foundation +import Library +import MacLibrary + +class IndependentApplicationDelegate: ApplicationDelegate { + public func applicationWillFinishLaunching(_: Notification) { + Variant.useSystemExtension = true + Task { + await setupSystemExtension() + } + } + + private nonisolated func setupSystemExtension() async { + do { + if await SystemExtension.isInstalled() { + if let result = try await SystemExtension.install() { + if result == .willCompleteAfterReboot { + return + } + } + } + } catch { + NSLog("setup system extension error: \(error.localizedDescription)") + } + } +} diff --git a/SFM.System/Info.plist b/SFM.System/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..78612700b0985afa475dedf92a55a2818514c0c4 --- /dev/null +++ b/SFM.System/Info.plist @@ -0,0 +1,106 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleURLTypes</key> + <array> + <dict> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + <key>CFBundleURLIconFile</key> + <string>AppIcon.icns</string> + <key>CFBundleURLName</key> + <string>sing-box</string> + <key>CFBundleURLSchemes</key> + <array> + <string>sing-box</string> + </array> + </dict> + </array> + <key>ITSAppUsesNonExemptEncryption</key> + <false/> + <key>NSUbiquitousContainers</key> + <dict> + <key>iCloud.io.nekohasekai.sfabeino</key> + <dict> + <key>NSUbiquitousContainerIsDocumentScopePublic</key> + <true/> + <key>NSUbiquitousContainerName</key> + <string>sing-box</string> + <key>NSUbiquitousContainerSupportedFolderLevels</key> + <string>Any</string> + </dict> + </dict> + <key>CFBundleDocumentTypes</key> + <array> + <dict> + <key>CFBundleTypeName</key> + <string>sing-box Profile</string> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + <key>LSHandlerRank</key> + <string>Owner</string> + <key>LSItemContentTypes</key> + <array> + <string>io.nekohasekai.sfabeino.profile</string> + </array> + <key>CFBundleTypeIconFile</key> + <string>AppIcon.icns</string> + </dict> + </array> + <key>UTExportedTypeDeclarations</key> + <array> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.item</string> + <string>public.content</string> + <string>public.data</string> + </array> + <key>UTTypeDescription</key> + <string>sing-box Profile</string> + <key>UTTypeIconFiles</key> + <array/> + <key>UTTypeIdentifier</key> + <string>io.nekohasekai.sfabeinoprofile</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>bpf</string> + </array> + </dict> + </dict> + </array> + <key>UTImportedTypeDeclarations</key> + <array> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.item</string> + <string>public.content</string> + <string>public.data</string> + </array> + <key>UTTypeDescription</key> + <string>sing-box Profile</string> + <key>UTTypeIconFiles</key> + <array> + <string>AppIcon.icns</string> + </array> + <key>UTTypeIdentifier</key> + <string>io.nekohasekai.sfabeino.profile</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>bpf</string> + </array> + </dict> + </dict> + </array> + <key>LSSupportsOpeningDocumentsInPlace</key> + <true/> + <key>UISupportsDocumentBrowser</key> + <false/> +</dict> +</plist> diff --git a/SFM.System/SFM.entitlements b/SFM.System/SFM.entitlements new file mode 100644 index 0000000000000000000000000000000000000000..63e350b5fdd5978b791eace76fc26a3f18e5a76c --- /dev/null +++ b/SFM.System/SFM.entitlements @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.developer.icloud-container-identifiers</key> + <array> + <string>iCloud.io.nekohasekai.sfabeino</string> + </array> + <key>com.apple.developer.icloud-services</key> + <array> + <string>CloudDocuments</string> + </array> + <key>com.apple.developer.networking.networkextension</key> + <array> + <string>packet-tunnel-provider-systemextension</string> + </array> + <key>com.apple.developer.system-extension.install</key> + <true/> + <key>com.apple.developer.ubiquity-container-identifiers</key> + <array> + <string>iCloud.io.nekohasekai.sfabeino</string> + </array> + <key>com.apple.security.app-sandbox</key> + <true/> + <key>com.apple.security.application-groups</key> + <array> + <string>group.io.nekohasekai.sfabeino</string> + </array> + <key>com.apple.security.files.user-selected.read-write</key> + <true/> + <key>com.apple.security.network.client</key> + <true/> +</dict> +</plist> diff --git a/SFM.System/Upload.plist b/SFM.System/Upload.plist new file mode 100644 index 0000000000000000000000000000000000000000..9b8a778e4ff502469b97971512cb27bfbd29f546 --- /dev/null +++ b/SFM.System/Upload.plist @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>destination</key> + <string>upload</string> + <key>method</key> + <string>developer-id</string> + <key>uploadSymbols</key> + <true/> + <key>provisioningProfiles</key> + <dict> + <key>io.nekohasekai.sfabeino.independent</key> + <string>XC io nekohasekai sfa independent</string> + <key>io.nekohasekai.sfabeino.system</key> + <string>XC io nekohasekai sfa system</string> + </dict> +</dict> +</plist> diff --git a/SFM/Application.swift b/SFM/Application.swift new file mode 100644 index 0000000000000000000000000000000000000000..e1eba06c68a86cf22869f69057391ac9e2fb1ce5 --- /dev/null +++ b/SFM/Application.swift @@ -0,0 +1,11 @@ +import MacLibrary +import SwiftUI + +@main +struct Application: App { + @NSApplicationDelegateAdaptor private var appDelegate: ApplicationDelegate + + var body: some Scene { + MacApplication() + } +} diff --git a/SFM/Info.plist b/SFM/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..858272fcc648719cd591e3e0d88cb8bbf0b0ea37 --- /dev/null +++ b/SFM/Info.plist @@ -0,0 +1,102 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDocumentTypes</key> + <array> + <dict> + <key>CFBundleTypeIconFile</key> + <string>AppIcon.icns</string> + <key>CFBundleTypeName</key> + <string>sing-box Profile</string> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + <key>LSHandlerRank</key> + <string>Owner</string> + <key>LSItemContentTypes</key> + <array> + <string>io.nekohasekai.sfabeino.profile</string> + </array> + </dict> + </array> + <key>CFBundleURLTypes</key> + <array> + <dict> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + <key>CFBundleURLIconFile</key> + <string>AppIcon</string> + <key>CFBundleURLName</key> + <string>sing-box</string> + <key>CFBundleURLSchemes</key> + <array> + <string>sing-box</string> + </array> + </dict> + </array> + <key>ITSAppUsesNonExemptEncryption</key> + <false/> + <key>NSUbiquitousContainers</key> + <dict> + <key>iCloud.io.nekohasekai.sfabeino</key> + <dict> + <key>NSUbiquitousContainerIsDocumentScopePublic</key> + <true/> + <key>NSUbiquitousContainerName</key> + <string>sing-box</string> + <key>NSUbiquitousContainerSupportedFolderLevels</key> + <string>Any</string> + </dict> + </dict> + <key>UTExportedTypeDeclarations</key> + <array> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.item</string> + <string>public.content</string> + <string>public.data</string> + </array> + <key>UTTypeDescription</key> + <string>sing-box Profile</string> + <key>UTTypeIconFiles</key> + <array/> + <key>UTTypeIdentifier</key> + <string>io.nekohasekai.sfabeino.profile</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>bpf</string> + </array> + </dict> + </dict> + </array> + <key>UTImportedTypeDeclarations</key> + <array> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.item</string> + <string>public.content</string> + <string>public.data</string> + </array> + <key>UTTypeDescription</key> + <string>sing-box Profile</string> + <key>UTTypeIconFiles</key> + <array> + <string>AppIcon.icns</string> + </array> + <key>UTTypeIdentifier</key> + <string>io.nekohasekai.sfabeino.profile</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>bpf</string> + </array> + </dict> + </dict> + </array> +</dict> +</plist> diff --git a/SFM/SFM.entitlements b/SFM/SFM.entitlements new file mode 100644 index 0000000000000000000000000000000000000000..179fc9f598807ed3c30e8021030b65dc138083bb --- /dev/null +++ b/SFM/SFM.entitlements @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.developer.icloud-container-identifiers</key> + <array> + <string>iCloud.io.nekohasekai.sfabeino</string> + </array> + <key>com.apple.developer.icloud-services</key> + <array> + <string>CloudDocuments</string> + </array> + <key>com.apple.developer.networking.networkextension</key> + <array> + <string>packet-tunnel-provider</string> + </array> + <key>com.apple.developer.ubiquity-container-identifiers</key> + <array> + <string>iCloud.io.nekohasekai.sfabeino</string> + </array> + <key>com.apple.security.app-sandbox</key> + <true/> + <key>com.apple.security.application-groups</key> + <array> + <string>group.io.nekohasekai.sfabeino</string> + </array> + <key>com.apple.security.files.user-selected.read-write</key> + <true/> + <key>com.apple.security.network.client</key> + <true/> +</dict> +</plist> diff --git a/SFT/Application.swift b/SFT/Application.swift new file mode 100644 index 0000000000000000000000000000000000000000..ec6c6add6ea060f2578ba44350c1634373606545 --- /dev/null +++ b/SFT/Application.swift @@ -0,0 +1,16 @@ +import Foundation +import Library +import SwiftUI + +@main +struct Application: App { + @UIApplicationDelegateAdaptor private var appDelegate: ApplicationDelegate + @StateObject private var environments = ExtensionEnvironments() + + var body: some Scene { + WindowGroup { + MainView() + .environmentObject(environments) + } + } +} diff --git a/SFT/ApplicationDelegate.swift b/SFT/ApplicationDelegate.swift new file mode 100644 index 0000000000000000000000000000000000000000..2c1fc83a7294e3c786b3cef53766ecadbb2cb4ec --- /dev/null +++ b/SFT/ApplicationDelegate.swift @@ -0,0 +1,23 @@ +import ApplicationLibrary +import Foundation +import Libbox +import Library +import UIKit + +class ApplicationDelegate: NSObject, UIApplicationDelegate { + func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { + NSLog("Here I stand") + LibboxSetup(FilePath.sharedDirectory.relativePath, FilePath.workingDirectory.relativePath, FilePath.cacheDirectory.relativePath, true) + setup() + return true + } + + private func setup() { + do { + try UIProfileUpdateTask.configure() + NSLog("setup background task success") + } catch { + NSLog("setup background task error: \(error.localizedDescription)") + } + } +} diff --git a/SFT/Assets.xcassets/AccentColor.colorset/Contents.json b/SFT/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..eb8789700816459c1e1480e0b34781d9fb78a1ca --- /dev/null +++ b/SFT/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..997b20e992378c89c27fac94fd0b9258e213a227 --- /dev/null +++ b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "tv - App Icon - AppStore - Back -1280 x 768 pt.png", + "idiom" : "tv" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/tv - App Icon - AppStore - Back -1280 x 768 pt.png b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/tv - App Icon - AppStore - Back -1280 x 768 pt.png new file mode 100644 index 0000000000000000000000000000000000000000..18b1f36c3b6ba9ceb4b9cab4f0ac595945330917 Binary files /dev/null and b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/tv - App Icon - AppStore - Back -1280 x 768 pt.png differ diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..73c00596a7fca3f3d4bdd64053b69d86745f9e10 --- /dev/null +++ b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..de59d885ae8d836da0247d15347fa53397f81254 --- /dev/null +++ b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "layers" : [ + { + "filename" : "Front.imagestacklayer" + }, + { + "filename" : "Middle.imagestacklayer" + }, + { + "filename" : "Back.imagestacklayer" + } + ] +} diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..2e003356c75081af56be6c8423bd90006315dbd9 --- /dev/null +++ b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,11 @@ +{ + "images" : [ + { + "idiom" : "tv" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..73c00596a7fca3f3d4bdd64053b69d86745f9e10 --- /dev/null +++ b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..a381da2b4e1beb2eb74b4256f886cd105c75314d --- /dev/null +++ b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "tv - App Icon - AppStore - Front -1280 x 768 pt.png", + "idiom" : "tv" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/tv - App Icon - AppStore - Front -1280 x 768 pt.png b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/tv - App Icon - AppStore - Front -1280 x 768 pt.png new file mode 100644 index 0000000000000000000000000000000000000000..64f87cf3f6713f5e851c79f4ccdc7d0a775f45ad Binary files /dev/null and b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/tv - App Icon - AppStore - Front -1280 x 768 pt.png differ diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..73c00596a7fca3f3d4bdd64053b69d86745f9e10 --- /dev/null +++ b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..70dbd872956e743c953c09a2a4bdc0fce0fe3ca4 --- /dev/null +++ b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,18 @@ +{ + "images" : [ + { + "filename" : "tv- App Icon Small - Back - 400 x 240 pt.png", + "idiom" : "tv", + "scale" : "1x" + }, + { + "filename" : "tv - App Icon Small - Back - 400 x 240 pt@2x.png", + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/tv - App Icon Small - Back - 400 x 240 pt@2x.png b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/tv - App Icon Small - Back - 400 x 240 pt@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fffedf69c67e4b5ddbdbcf59afdc2369c521af68 Binary files /dev/null and b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/tv - App Icon Small - Back - 400 x 240 pt@2x.png differ diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/tv- App Icon Small - Back - 400 x 240 pt.png b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/tv- App Icon Small - Back - 400 x 240 pt.png new file mode 100644 index 0000000000000000000000000000000000000000..7a8fd6500a728e28cf64e6afee5471a95f295eef Binary files /dev/null and b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/tv- App Icon Small - Back - 400 x 240 pt.png differ diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..73c00596a7fca3f3d4bdd64053b69d86745f9e10 --- /dev/null +++ b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..de59d885ae8d836da0247d15347fa53397f81254 --- /dev/null +++ b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "layers" : [ + { + "filename" : "Front.imagestacklayer" + }, + { + "filename" : "Middle.imagestacklayer" + }, + { + "filename" : "Back.imagestacklayer" + } + ] +} diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..795cce17243c4963342d31db5b85681fba11e322 --- /dev/null +++ b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..73c00596a7fca3f3d4bdd64053b69d86745f9e10 --- /dev/null +++ b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..377927a7da0694d718c7d980e8e9b26df1e07322 --- /dev/null +++ b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,18 @@ +{ + "images" : [ + { + "filename" : "tv- App Icon Small - Front - 400 x 240 pt.png", + "idiom" : "tv", + "scale" : "1x" + }, + { + "filename" : "tv - App Icon Small - Front - 400 x 240 pt@2x.png", + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/tv - App Icon Small - Front - 400 x 240 pt@2x.png b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/tv - App Icon Small - Front - 400 x 240 pt@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a9e505080ef4d4fa390380688c4e3526b9d58b7e Binary files /dev/null and b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/tv - App Icon Small - Front - 400 x 240 pt@2x.png differ diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/tv- App Icon Small - Front - 400 x 240 pt.png b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/tv- App Icon Small - Front - 400 x 240 pt.png new file mode 100644 index 0000000000000000000000000000000000000000..b6fb473a2d20776a2387f85dae0207a6ffec8b6a Binary files /dev/null and b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/tv- App Icon Small - Front - 400 x 240 pt.png differ diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..73c00596a7fca3f3d4bdd64053b69d86745f9e10 --- /dev/null +++ b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..f47ba43daac4c9d6dbfc2b67bdaa24db32b3e8e9 --- /dev/null +++ b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json @@ -0,0 +1,32 @@ +{ + "assets" : [ + { + "filename" : "App Icon - App Store.imagestack", + "idiom" : "tv", + "role" : "primary-app-icon", + "size" : "1280x768" + }, + { + "filename" : "App Icon.imagestack", + "idiom" : "tv", + "role" : "primary-app-icon", + "size" : "400x240" + }, + { + "filename" : "Top Shelf Image Wide.imageset", + "idiom" : "tv", + "role" : "top-shelf-image-wide", + "size" : "2320x720" + }, + { + "filename" : "Top Shelf Image.imageset", + "idiom" : "tv", + "role" : "top-shelf-image", + "size" : "1920x720" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..4ea4d99f655e9a0586aa774f3626b36a8e28f62e --- /dev/null +++ b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json @@ -0,0 +1,18 @@ +{ + "images" : [ + { + "filename" : "tv - Top Shelf - Wide - 2320 x 720 pt.png", + "idiom" : "tv", + "scale" : "1x" + }, + { + "filename" : "tv - Top Shelf - Wide - 2320 x 720 pt@2x.png", + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/tv - Top Shelf - Wide - 2320 x 720 pt.png b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/tv - Top Shelf - Wide - 2320 x 720 pt.png new file mode 100644 index 0000000000000000000000000000000000000000..f09550ae8e2c1b2e891a63169c3cb0359f03d440 Binary files /dev/null and b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/tv - Top Shelf - Wide - 2320 x 720 pt.png differ diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/tv - Top Shelf - Wide - 2320 x 720 pt@2x.png b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/tv - Top Shelf - Wide - 2320 x 720 pt@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2dc838014cd79ef12c2aefba8310800e2fc29dd9 Binary files /dev/null and b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/tv - Top Shelf - Wide - 2320 x 720 pt@2x.png differ diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/1920 x 720 pt.png b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/1920 x 720 pt.png new file mode 100644 index 0000000000000000000000000000000000000000..084f2befee567f6721d0b4ce85ad623d8b13d907 Binary files /dev/null and b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/1920 x 720 pt.png differ diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/1920 x 720 pt@2x.png b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/1920 x 720 pt@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ee565e1576c7c08dba777214e9f2023d33f559fc Binary files /dev/null and b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/1920 x 720 pt@2x.png differ diff --git a/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..940b25a508ebd54662897dfefe697d3c85a9bb6a --- /dev/null +++ b/SFT/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json @@ -0,0 +1,18 @@ +{ + "images" : [ + { + "filename" : "1920 x 720 pt.png", + "idiom" : "tv", + "scale" : "1x" + }, + { + "filename" : "1920 x 720 pt@2x.png", + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SFT/Assets.xcassets/Contents.json b/SFT/Assets.xcassets/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..73c00596a7fca3f3d4bdd64053b69d86745f9e10 --- /dev/null +++ b/SFT/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SFT/ContentView.swift b/SFT/ContentView.swift new file mode 100644 index 0000000000000000000000000000000000000000..750338aef322c779d2f162c9e49ad3ccef84a7c1 --- /dev/null +++ b/SFT/ContentView.swift @@ -0,0 +1,61 @@ +import ApplicationLibrary +import Library +import SwiftUI + +struct ContentView: View { + @Environment(\.selection) private var selection + @Environment(\.extensionProfile) private var extensionProfile + + var body: some View { + viewBuilder { + if let profile = extensionProfile.wrappedValue { + ContentView0().environmentObject(profile) + } else { + ContentView1() + } + } + } + + struct ContentView0: View { + @Environment(\.selection) private var selection + @EnvironmentObject private var extensionProfile: ExtensionProfile + + var body: some View { + TabView(selection: selection) { + ForEach(NavigationPage.allCases.filter { it in + it.visible(extensionProfile) + }, id: \.self) { page in + NavigationStackCompat { + page.contentView + .focusSection() + } + .tag(page) + .tabItem { page.label } + } + }.onChangeCompat(of: extensionProfile.status) { + if !selection.wrappedValue.visible(extensionProfile) { + selection.wrappedValue = NavigationPage.dashboard + } + } + } + } + + struct ContentView1: View { + @Environment(\.selection) private var selection + + var body: some View { + TabView(selection: selection) { + ForEach(NavigationPage.allCases.filter { it in + it.visible(nil) + }, id: \.self) { page in + NavigationStackCompat { + page.contentView + .focusSection() + } + .tag(page) + .tabItem { page.label } + } + } + } + } +} diff --git a/SFT/Info.plist b/SFT/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..846b9d428f95167b1d5039c23cf954988b36afaa --- /dev/null +++ b/SFT/Info.plist @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>UIRequiredDeviceCapabilities</key> + <array> + <string>arm64</string> + </array> + <key>NSApplicationServices</key> + <dict> + <key>Browses</key> + <array> + <dict> + <key>NSApplicationServiceIdentifier</key> + <string>sing-box:profile</string> + <key>NSApplicationServiceUsageDescription</key> + <string>Import sing-box profile from other devices</string> + <key>NSApplicationServicePlatformSupport</key> + <array> + <string>iOS</string> + <string>iPadOS</string> + </array> + </dict> + </array> + </dict> + <key>BGTaskSchedulerPermittedIdentifiers</key> + <array> + <string>io.nekohasekai.sfabeino.update_profiles</string> + </array> + <key>CFBundleURLTypes</key> + <array> + <dict> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + <key>CFBundleURLIconFile</key> + <string>AppIcon.icns</string> + <key>CFBundleURLName</key> + <string>sing-box</string> + <key>CFBundleURLSchemes</key> + <array> + <string>sing-box</string> + </array> + </dict> + </array> + <key>ITSAppUsesNonExemptEncryption</key> + <false/> + <key>UIBackgroundModes</key> + <array> + <string>fetch</string> + </array> +</dict> +</plist> diff --git a/SFT/MainView.swift b/SFT/MainView.swift new file mode 100644 index 0000000000000000000000000000000000000000..ab283aafc2130685ae19c2d3530215948c04077c --- /dev/null +++ b/SFT/MainView.swift @@ -0,0 +1,37 @@ +import ApplicationLibrary +import Libbox +import Library +import SwiftUI + +struct MainView: View { + @Environment(\.scenePhase) private var scenePhase + @EnvironmentObject private var environments: ExtensionEnvironments + @State private var selection = NavigationPage.dashboard + + var body: some View { + TabView(selection: $selection) { + ForEach(NavigationPage.allCases, id: \.self) { page in + NavigationStackCompat { + page.contentView + .focusSection() + } + .tag(page) + .tabItem { page.label } + } + } + .onAppear { + environments.postReload() + } + .onChangeCompat(of: scenePhase) { newValue in + if newValue == .active { + environments.postReload() + } + } + .onChangeCompat(of: selection) { newValue in + if newValue == .logs { + environments.connectLog() + } + } + .environment(\.selection, $selection) + } +} diff --git a/SFT/SFT.entitlements b/SFT/SFT.entitlements new file mode 100644 index 0000000000000000000000000000000000000000..ea261188583265ae6a92e0ea13dfa6ff8c6ab9f3 --- /dev/null +++ b/SFT/SFT.entitlements @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.developer.networking.networkextension</key> + <array> + <string>packet-tunnel-provider</string> + </array> + <key>com.apple.security.application-groups</key> + <array> + <string>group.io.nekohasekai.sfabeino</string> + </array> +</dict> +</plist> diff --git a/SystemExtension/Info.plist b/SystemExtension/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..7c6ff9593f9b1b802fdcf394b7bee336f1c5c285 --- /dev/null +++ b/SystemExtension/Info.plist @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>NetworkExtension</key> + <dict> + <key>NEMachServiceName</key> + <string>group.io.nekohasekai.sfabeino.system</string> + <key>NEProviderClasses</key> + <dict> + <key>com.apple.networkextension.packet-tunnel</key> + <string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string> + </dict> + </dict> +</dict> +</plist> diff --git a/SystemExtension/PacketTunnelProvider.swift b/SystemExtension/PacketTunnelProvider.swift new file mode 100644 index 0000000000000000000000000000000000000000..4db300982a262cf0bfe531f59a4bae8b5a51ee4f --- /dev/null +++ b/SystemExtension/PacketTunnelProvider.swift @@ -0,0 +1,16 @@ +import Library +import NetworkExtension + +class PacketTunnelProvider: ExtensionProvider { + override func startTunnel(options: [String: NSObject]?) async throws { + guard let usernameObject = options?["username"] else { + writeFatalError("missing start options") + return + } + let username = usernameObject as! NSString + FilePath.sharedDirectory = URL(filePath: "/Users/\(username)/Library/Group Containers/\(FilePath.groupName)") + FilePath.iCloudDirectory = URL(filePath: "/Users/\(username)/Library/Mobile Documents/iCloud~\(FilePath.packageName.replacingOccurrences(of: ".", with: "~"))").appendingPathComponent("Documents", isDirectory: true) + self.username = String(username) + try await super.startTunnel(options: options) + } +} diff --git a/SystemExtension/SystemExtension.entitlements b/SystemExtension/SystemExtension.entitlements new file mode 100644 index 0000000000000000000000000000000000000000..2d5b705c4559c742526dfdcc3c35b8c533de3e7d --- /dev/null +++ b/SystemExtension/SystemExtension.entitlements @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.developer.networking.networkextension</key> + <array> + <string>packet-tunnel-provider-systemextension</string> + </array> + <key>com.apple.security.application-groups</key> + <array> + <string>group.io.nekohasekai.sfabeino</string> + </array> + <key>com.apple.security.app-sandbox</key> + <false/> +</dict> +</plist> diff --git a/SystemExtension/main.swift b/SystemExtension/main.swift new file mode 100644 index 0000000000000000000000000000000000000000..5b415e814560a135cd7a6a379b783348649067a5 --- /dev/null +++ b/SystemExtension/main.swift @@ -0,0 +1,11 @@ +import Foundation +import Library +import NetworkExtension + +Variant.useSystemExtension = true + +autoreleasepool { + NEProvider.startSystemExtensionMode() +} + +dispatchMain() diff --git a/TVExtension/Info.plist b/TVExtension/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..a1888188bcf26b0457a6458bbe77a6c209ae1607 --- /dev/null +++ b/TVExtension/Info.plist @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>UIRequiredDeviceCapabilities</key> + <array> + <string>arm64</string> + </array> + <key>NSExtension</key> + <dict> + <key>NSExtensionPointIdentifier</key> + <string>com.apple.networkextension.packet-tunnel</string> + <key>NSExtensionPrincipalClass</key> + <string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string> + </dict> +</dict> +</plist> diff --git a/TVExtension/PacketTunnelProvider.swift b/TVExtension/PacketTunnelProvider.swift new file mode 100644 index 0000000000000000000000000000000000000000..fbeef8d7e8d6bcb5aa6390ee9fd0015765754d36 --- /dev/null +++ b/TVExtension/PacketTunnelProvider.swift @@ -0,0 +1,4 @@ +import Foundation +import Library + +class PacketTunnelProvider: ExtensionProvider {} diff --git a/TVExtension/TVExtension.entitlements b/TVExtension/TVExtension.entitlements new file mode 100644 index 0000000000000000000000000000000000000000..ea261188583265ae6a92e0ea13dfa6ff8c6ab9f3 --- /dev/null +++ b/TVExtension/TVExtension.entitlements @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.developer.networking.networkextension</key> + <array> + <string>packet-tunnel-provider</string> + </array> + <key>com.apple.security.application-groups</key> + <array> + <string>group.io.nekohasekai.sfabeino</string> + </array> +</dict> +</plist> diff --git a/WidgetExtension/Assets.xcassets/AccentColor.colorset/Contents.json b/WidgetExtension/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..eb8789700816459c1e1480e0b34781d9fb78a1ca --- /dev/null +++ b/WidgetExtension/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WidgetExtension/Assets.xcassets/AppIcon.appiconset/Contents.json b/WidgetExtension/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..13613e3ee1a9348462a11a77a619faa808a346eb --- /dev/null +++ b/WidgetExtension/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WidgetExtension/Assets.xcassets/Contents.json b/WidgetExtension/Assets.xcassets/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..73c00596a7fca3f3d4bdd64053b69d86745f9e10 --- /dev/null +++ b/WidgetExtension/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WidgetExtension/Assets.xcassets/WidgetBackground.colorset/Contents.json b/WidgetExtension/Assets.xcassets/WidgetBackground.colorset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..eb8789700816459c1e1480e0b34781d9fb78a1ca --- /dev/null +++ b/WidgetExtension/Assets.xcassets/WidgetBackground.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WidgetExtension/Info.plist b/WidgetExtension/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..0f118fb75e45dec47229aabe33fb05eb7eb4f31a --- /dev/null +++ b/WidgetExtension/Info.plist @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>NSExtension</key> + <dict> + <key>NSExtensionPointIdentifier</key> + <string>com.apple.widgetkit-extension</string> + </dict> +</dict> +</plist> diff --git a/WidgetExtension/Intents.swift b/WidgetExtension/Intents.swift new file mode 100644 index 0000000000000000000000000000000000000000..6be7028f21a9e0c55b3acc242cec1df7ae0c238b --- /dev/null +++ b/WidgetExtension/Intents.swift @@ -0,0 +1,46 @@ +import AppIntents +import Library +import WidgetKit + +struct ConfigurationIntent: WidgetConfigurationIntent { + static var title: LocalizedStringResource = "Configuration" + static var description = IntentDescription("Configuration sing-bix widget.") +} + +struct StartServiceIntent: AppIntent { + static var title: LocalizedStringResource = "Start sing-box" + + static var description = + IntentDescription("Start sing-box servie") + + func perform() async throws -> some IntentResult { + guard let extensionProfile = try await (ExtensionProfile.load()) else { + throw NSError(domain: "NetworkExtension not installed", code: 0) + } + if extensionProfile.status == .connected { + return .result() + } else { + try await extensionProfile.start() + } + return .result() + } +} + +struct StopServiceIntent: AppIntent { + static var title: LocalizedStringResource = "Stop sing-box" + + static var description = + IntentDescription("Stop sing-box service") + + static var parameterSummary: some ParameterSummary { + Summary("Stop sing-box service") + } + + func perform() async throws -> some IntentResult { + guard let extensionProfile = try await (ExtensionProfile.load()) else { + return .result() + } + extensionProfile.stop() + return .result() + } +} diff --git a/WidgetExtension/WidgetExtension.entitlements b/WidgetExtension/WidgetExtension.entitlements new file mode 100644 index 0000000000000000000000000000000000000000..6524ff10fe67339b5e6e63bafaa93d596e5f9b4c --- /dev/null +++ b/WidgetExtension/WidgetExtension.entitlements @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.security.application-groups</key> + <array> + <string>group.org.sagernet.sfa</string> + </array> +</dict> +</plist> diff --git a/WidgetExtension/WidgetExtension.swift b/WidgetExtension/WidgetExtension.swift new file mode 100644 index 0000000000000000000000000000000000000000..5ba87773fe55db40f27914c1e1f43ffab32c9624 --- /dev/null +++ b/WidgetExtension/WidgetExtension.swift @@ -0,0 +1,89 @@ +import AppIntents +import Libbox +import Library +import SwiftUI +import WidgetKit + +struct Provider: AppIntentTimelineProvider { + func placeholder(in _: Context) -> ExtensionStatus { + ExtensionStatus(date: .now, isConnected: false, profileList: []) + } + + func snapshot(for _: ConfigurationIntent, in _: Context) async -> ExtensionStatus { + var status = ExtensionStatus(date: .now, isConnected: false, profileList: []) + + do { + status.isConnected = try await ExtensionProfile.load()?.status.isStrictConnected ?? false + + let profileList = try ProfileManager.list() + let selectedProfileID = SharedPreferences.selectedProfileID + for profile in profileList { + status.profileList.append(ProfileEntry(profile: profile, isSelected: profile.id == selectedProfileID)) + } + } catch {} + + return status + } + + func timeline(for intent: ConfigurationIntent, in context: Context) async -> Timeline<ExtensionStatus> { + await Timeline(entries: [snapshot(for: intent, in: context)], policy: .never) + } +} + +struct ExtensionStatus: TimelineEntry { + var date: Date + var isConnected: Bool + var profileList: [ProfileEntry] +} + +struct ProfileEntry { + let profile: Profile + let isSelected: Bool +} + +struct WidgetView: View { + @Environment(\.widgetFamily) private var family + + var status: ExtensionStatus + + var body: some View { + VStack { + LabeledContent { + Text(LibboxVersion()) + .font(.caption) + } label: { + Text("sing-box") + .font(.headline) + } + VStack { + viewBuilder { + if !status.isConnected { + Button(intent: StartServiceIntent()) { + Image(systemName: "play.fill") + } + } else { + Button(intent: StopServiceIntent()) { + Image(systemName: "stop.fill") + } + } + } + .controlSize(.large) + .invalidatableContent() + } + .frame(maxHeight: .infinity, alignment: .center) + } + .frame(maxHeight: .infinity, alignment: .topLeading) + .containerBackground(.fill.tertiary, for: .widget) + } +} + +struct WidgetExtension: Widget { + @State private var extensionProfile: ExtensionProfile? + + var body: some WidgetConfiguration { + AppIntentConfiguration(kind: "sing-box", intent: ConfigurationIntent.self, provider: Provider()) { status in + WidgetView(status: status) + } + .supportedFamilies([.systemSmall]) + } +} diff --git a/WidgetExtension/WidgetExtensionBundle.swift b/WidgetExtension/WidgetExtensionBundle.swift new file mode 100644 index 0000000000000000000000000000000000000000..fab679bd1351257d6aaa3332650fde3d7bc90eb7 --- /dev/null +++ b/WidgetExtension/WidgetExtensionBundle.swift @@ -0,0 +1,9 @@ +import SwiftUI +import WidgetKit + +@main +struct WidgetExtensionBundle: WidgetBundle { + var body: some Widget { + WidgetExtension() + } +} diff --git a/sing-box.xcodeproj/project.pbxproj b/sing-box.xcodeproj/project.pbxproj new file mode 100644 index 0000000000000000000000000000000000000000..1e2bb1853e38aab3ebdb2b10a4cb466d28b8ebd1 --- /dev/null +++ b/sing-box.xcodeproj/project.pbxproj @@ -0,0 +1,1766 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 3A017F922A4AB2E4009149FA /* GRDB in Frameworks */ = {isa = PBXBuildFile; productRef = 3A017F912A4AB2E4009149FA /* GRDB */; }; + 3A0C6D3C2A79D46500A4DF2B /* OverviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A0C6D3B2A79D46500A4DF2B /* OverviewView.swift */; }; + 3A0C6D3E2A79D6A600A4DF2B /* DashboardPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A0C6D3D2A79D6A600A4DF2B /* DashboardPage.swift */; }; + 3A172D2B2B88E9DB00D98050 /* BackButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A172D2A2B88E9DB00D98050 /* BackButton.swift */; }; + 3A1CF2F02A50E5EE000A8289 /* GroupListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1CF2EF2A50E5EE000A8289 /* GroupListView.swift */; }; + 3A1CF2F22A50E613000A8289 /* OutboundGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1CF2F12A50E613000A8289 /* OutboundGroup.swift */; }; + 3A1CF2F62A50EE9C000A8289 /* GroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1CF2F52A50EE9C000A8289 /* GroupView.swift */; }; + 3A1CF2F82A50F0A5000A8289 /* GroupItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1CF2F72A50F0A5000A8289 /* GroupItemView.swift */; }; + 3A1CF2FA2A50F0BD000A8289 /* OutboundGroupItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1CF2F92A50F0BD000A8289 /* OutboundGroupItem.swift */; }; + 3A2223562A6E1BDE00C50B23 /* Variant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A2223552A6E1BDE00C50B23 /* Variant.swift */; }; + 3A22235A2A6E212A00C50B23 /* SystemExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A2223592A6E212A00C50B23 /* SystemExtension.swift */; }; + 3A27D9002A89BE230031EBCC /* CommandClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A27D8FF2A89BE230031EBCC /* CommandClient.swift */; }; + 3A27D9022A89C6870031EBCC /* ExtensionEnvironments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A27D9012A89C6870031EBCC /* ExtensionEnvironments.swift */; }; + 3A3AA7FC2A4EFDAE002F78AB /* Library.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; }; + 3A3AA7FF2A4EFDB3002F78AB /* Library.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; }; + 3A3AB2A72B70C146001815AE /* CoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3AB2A62B70C146001815AE /* CoreView.swift */; }; + 3A3AB2A92B70C5F1001815AE /* RequestReviewButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3AB2A82B70C5F1001815AE /* RequestReviewButton.swift */; }; + 3A3DEBEB2A4FFE2D00373BF4 /* AppIntents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A3DEBE62A4FFA6000373BF4 /* AppIntents.framework */; }; + 3A411CEC2B734959000D9501 /* MacAppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A411CEB2B734959000D9501 /* MacAppView.swift */; }; + 3A44BB822A4DC28700E4C9F8 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A44BB812A4DC28700E4C9F8 /* MainView.swift */; }; + 3A4A020D2B53E3DC004EFB87 /* QRCode in Frameworks */ = {isa = PBXBuildFile; productRef = 3A4A020C2B53E3DC004EFB87 /* QRCode */; }; + 3A4EAD1B2A4FEB02005435B3 /* Library.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; }; + 3A4EAD212A4FEB3C005435B3 /* ApplicationLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4EAD202A4FEB3C005435B3 /* ApplicationLibrary.swift */; }; + 3A4EAD222A4FEB54005435B3 /* NavigationPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEC21742A45B0B800A63465 /* NavigationPage.swift */; }; + 3A4EAD232A4FEB5A005435B3 /* EnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEC21782A45BA5300A63465 /* EnvironmentValues.swift */; }; + 3A4EAD242A4FEB65005435B3 /* InstallProfileButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF342A22A4A9B9B002B34AC /* InstallProfileButton.swift */; }; + 3A4EAD252A4FEB65005435B3 /* StartStopButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF342CC2A4AA88C002B34AC /* StartStopButton.swift */; }; + 3A4EAD262A4FEB65005435B3 /* ExtensionStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF342D02A4AACC4002B34AC /* ExtensionStatusView.swift */; }; + 3A4EAD272A4FEB65005435B3 /* DashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADF8DF32A4AF9B500900CC8 /* DashboardView.swift */; }; + 3A4EAD282A4FEB65005435B3 /* ActiveDashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADF8DF12A4AF59900900CC8 /* ActiveDashboardView.swift */; }; + 3A4EAD292A4FEB6D005435B3 /* FormItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF342D32A4AADB2002B34AC /* FormItem.swift */; }; + 3A4EAD2A2A4FEB6D005435B3 /* Binding+Unwrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADF8E022A4B118700900CC8 /* Binding+Unwrap.swift */; }; + 3A4EAD2B2A4FEB6D005435B3 /* ViewBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7E90302A46745A00D53052 /* ViewBuilder.swift */; }; + 3A4EAD2D2A4FEB77005435B3 /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADF8DF62A4AFB2C00900CC8 /* ProfileView.swift */; }; + 3A4EAD2E2A4FEB77005435B3 /* EditProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADF8E002A4B0F6300900CC8 /* EditProfileView.swift */; }; + 3A4EAD2F2A4FEB77005435B3 /* EditProfileContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAB5E752A4BFB0B009757F1 /* EditProfileContentView.swift */; }; + 3A4EAD302A4FEB77005435B3 /* NewProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADF8DF82A4AFCB400900CC8 /* NewProfileView.swift */; }; + 3A4EAD322A4FEB7B005435B3 /* ServiceLogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAB5E732A4BF90B009757F1 /* ServiceLogView.swift */; }; + 3A4EAD342A4FEB7F005435B3 /* LogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA1ABB92A4C4054000FD4BA /* LogView.swift */; }; + 3A4EAD352A4FEB9C005435B3 /* UIProfileUpdateTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A55F9572A4D137E003C4EF4 /* UIProfileUpdateTask.swift */; }; + 3A4EAD362A4FEB9C005435B3 /* ProfileUpdateTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A55F9592A4D1554003C4EF4 /* ProfileUpdateTask.swift */; }; + 3A4EAD372A4FEC20005435B3 /* ApplicationLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A4EAD102A4FEAE6005435B3 /* ApplicationLibrary.framework */; }; + 3A4EAD3C2A4FECCE005435B3 /* NEVPNStatus+isConnected.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4EAD3B2A4FECCE005435B3 /* NEVPNStatus+isConnected.swift */; }; + 3A4F68B02A97602C003D66D3 /* ClashModeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4F68AF2A97602C003D66D3 /* ClashModeView.swift */; }; + 3A57DF372A4D5D2600690BC5 /* Profile+Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A57DF362A4D5D2600690BC5 /* Profile+Date.swift */; }; + 3A57DF422A4D927A00690BC5 /* Profile+Hashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A57DF412A4D927A00690BC5 /* Profile+Hashable.swift */; }; + 3A60CC272B70880100D2D682 /* PacketTunnelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A60CC262B70880100D2D682 /* PacketTunnelView.swift */; }; + 3A60CC292B70A7C400D2D682 /* Color+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A60CC282B70A7C400D2D682 /* Color+Extension.swift */; }; + 3A60CC2B2B70AD6700D2D682 /* SettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A60CC2A2B70AD6700D2D682 /* SettingView.swift */; }; + 3A648D2D2A4EEAA600D95A12 /* Library.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A648D2C2A4EEAA600D95A12 /* Library.swift */; }; + 3A648D542A4EF4C700D95A12 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF342B12A4AA520002B34AC /* NetworkExtension.framework */; }; + 3A6CA4542BC19FDE0012B238 /* OnDemandRulesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A6CA4532BC19FDE0012B238 /* OnDemandRulesView.swift */; }; + 3A6CA5A92A713C420027933B /* AppIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 3A6CA5A52A713AA10027933B /* AppIcon.icns */; }; + 3A76504C2A4F08BA003945C5 /* Libbox.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC20DB2A4599D000A63465 /* Libbox.xcframework */; }; + 3A7701702A4E6B34008F031F /* IntentsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A77016F2A4E6B34008F031F /* IntentsExtension.swift */; }; + 3A7701722A4E6B34008F031F /* Intents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7701712A4E6B34008F031F /* Intents.swift */; }; + 3A7904502B6E7BAC006C08D5 /* SponsorsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A79044F2B6E7BAC006C08D5 /* SponsorsView.swift */; }; + 3A7E90352A46756300D53052 /* SharedPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7E90342A46756300D53052 /* SharedPreferences.swift */; }; + 3A7E90382A46778E00D53052 /* BinaryCodable in Frameworks */ = {isa = PBXBuildFile; productRef = 3A7E90372A46778E00D53052 /* BinaryCodable */; }; + 3A8655142A4FA26600B7181F /* IntentsExtension.appex in Embed ExtensionKit Extensions */ = {isa = PBXBuildFile; fileRef = 3A77016D2A4E6B34008F031F /* IntentsExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 3A9144D92A46AE370036E9AD /* ShadredPreferences+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A9144D82A46AE370036E9AD /* ShadredPreferences+Database.swift */; }; + 3A9759202A4EB69C00E4404B /* Library.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; }; + 3A9759212A4EB69C00E4404B /* Library.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 3A99B42A2A7526990010D4B0 /* NavigationStackCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A99B4292A7526990010D4B0 /* NavigationStackCompat.swift */; }; + 3A99B42C2A75288C0010D4B0 /* ViewCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A99B42B2A75288C0010D4B0 /* ViewCompat.swift */; }; + 3A99B42E2A752ABB0010D4B0 /* NavigationDestinationCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A99B42D2A752ABB0010D4B0 /* NavigationDestinationCompat.swift */; }; + 3AABFD432A9CC5A7005A24A4 /* Upload.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3AABFD422A9CC5A7005A24A4 /* Upload.plist */; }; + 3AB1220B2A70FD500087CD55 /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB1220A2A70FD500087CD55 /* Alert.swift */; }; + 3AC1944F2A50247300BD8CB9 /* ApplicationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AC1944E2A50247300BD8CB9 /* ApplicationDelegate.swift */; }; + 3AC5EC082A6417470077AF34 /* DeviceCensorship.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AC5EC072A6417470077AF34 /* DeviceCensorship.swift */; }; + 3AC729F02A75D9D000FE8EC1 /* Profile+Transferable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AC729EF2A75D9D000FE8EC1 /* Profile+Transferable.swift */; }; + 3AC729F22A76088E00FE8EC1 /* ShareButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AC729F12A76088E00FE8EC1 /* ShareButton.swift */; }; + 3AC8CF9B2A736C750002AF3C /* ImportProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AC8CF9A2A736C750002AF3C /* ImportProfileView.swift */; }; + 3ACA8B332B7E037800B7238F /* DeleteButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ACA8B322B7E037800B7238F /* DeleteButton.swift */; }; + 3ACE6DE32ACADF55009D9A8A /* Binding+Setter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ACE6DE22ACADF55009D9A8A /* Binding+Setter.swift */; }; + 3AD0953D2A70EB310052764E /* Profile+Share.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AD0953C2A70EB310052764E /* Profile+Share.swift */; }; + 3ADBB4252A7389640041D44F /* ProfileServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADBB4242A7389640041D44F /* ProfileServer.swift */; }; + 3ADBB42A2A73A7060041D44F /* NWSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADBB4292A73A7060041D44F /* NWSocket.swift */; }; + 3AE4D0B22A6E2B6A009FEA9E /* ExtensionPlatformInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF342A62A4AA0FF002B34AC /* ExtensionPlatformInterface.swift */; }; + 3AE4D0B32A6E2B94009FEA9E /* Extension+RunBlocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF342A82A4AA155002B34AC /* Extension+RunBlocking.swift */; }; + 3AE4D0B42A6E2BA3009FEA9E /* Extension+Iterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF342AA2A4AA173002B34AC /* Extension+Iterator.swift */; }; + 3AE4D0B52A6E2BAC009FEA9E /* ExtensionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A096F892A4ED3DE00D4A2ED /* ExtensionProvider.swift */; }; + 3AE4D0B72A6E2C01009FEA9E /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE4D0B62A6E2C01009FEA9E /* PacketTunnelProvider.swift */; }; + 3AE4D0C12A6E4852009FEA9E /* InstallSystemExtensionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE4D0C02A6E4852009FEA9E /* InstallSystemExtensionButton.swift */; }; + 3AEAEE992A4F16430059612D /* Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 3A096F862A4ED3DE00D4A2ED /* Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 3AEC20F62A459AB400A63465 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEC20F52A459AB400A63465 /* Application.swift */; }; + 3AEC20FA2A459AB500A63465 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3AEC20F92A459AB500A63465 /* Assets.xcassets */; }; + 3AEC212F2A459D5600A63465 /* Profile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEC212E2A459D5600A63465 /* Profile.swift */; }; + 3AEC213C2A459FDF00A63465 /* Databse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEC213B2A459FDF00A63465 /* Databse.swift */; }; + 3AEC21402A45A28F00A63465 /* ProfileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEC213F2A45A28F00A63465 /* ProfileManager.swift */; }; + 3AEC21422A45A8FF00A63465 /* Profile+Update.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEC21412A45A8FF00A63465 /* Profile+Update.swift */; }; + 3AEC21452A45A93800A63465 /* HTTPClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEC21442A45A93800A63465 /* HTTPClient.swift */; }; + 3AEC21482A45A9DE00A63465 /* Bundle+Version.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEC21472A45A9DE00A63465 /* Bundle+Version.swift */; }; + 3AEC214A2A45AA5600A63465 /* Profile+RW.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEC21492A45AA5600A63465 /* Profile+RW.swift */; }; + 3AEC214C2A45AA8E00A63465 /* FilePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEC214B2A45AA8E00A63465 /* FilePath.swift */; }; + 3AF342A02A4A9916002B34AC /* ExtensionProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF3429F2A4A9916002B34AC /* ExtensionProfile.swift */; }; + 3AF3A3D22B2207F3001FD7C1 /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF3A3D12B2207E1001FD7C1 /* libresolv.tbd */; }; + 3AF5E3E72B6F90640058B9E8 /* ProfileOverrideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF5E3E62B6F90640058B9E8 /* ProfileOverrideView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 3A44BB7E2A4DC1D800E4C9F8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 3AEC20BD2A45991900A63465 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3A096F852A4ED3DE00D4A2ED; + remoteInfo = Extension; + }; + 3A4EAD1D2A4FEB02005435B3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 3AEC20BD2A45991900A63465 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3AEC211C2A459B4700A63465; + remoteInfo = Library; + }; + 3A4EAD392A4FEC20005435B3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 3AEC20BD2A45991900A63465 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3A4EAD0F2A4FEAE6005435B3; + remoteInfo = ApplicationLibrary; + }; + 3A76504A2A4F07F6003945C5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 3AEC20BD2A45991900A63465 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3AEC211C2A459B4700A63465; + remoteInfo = Library; + }; + 3A77017D2A4E6B5E008F031F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 3AEC20BD2A45991900A63465 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3AEC211C2A459B4700A63465; + remoteInfo = Library; + }; + 3A8655152A4FA26600B7181F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 3AEC20BD2A45991900A63465 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3A77016C2A4E6B34008F031F; + remoteInfo = IntentsExtension; + }; + 3AEAEE9A2A4F16430059612D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 3AEC20BD2A45991900A63465 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3A096F852A4ED3DE00D4A2ED; + remoteInfo = Extension; + }; + 3AEC21372A459E0A00A63465 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 3AEC20BD2A45991900A63465 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3AEC211C2A459B4700A63465; + remoteInfo = Library; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 3A44BB782A4DC17000E4C9F8 /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 3AEAEE992A4F16430059612D /* Extension.appex in Embed Foundation Extensions */, + ); + name = "Embed Foundation Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; + 3A8655172A4FA26600B7181F /* Embed ExtensionKit Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "$(EXTENSIONS_FOLDER_PATH)"; + dstSubfolderSpec = 16; + files = ( + 3A8655142A4FA26600B7181F /* IntentsExtension.appex in Embed ExtensionKit Extensions */, + ); + name = "Embed ExtensionKit Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; + 3A9759222A4EB69C00E4404B /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 3A9759212A4EB69C00E4404B /* Library.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 3A096F862A4ED3DE00D4A2ED /* Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = Extension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 3A096F892A4ED3DE00D4A2ED /* ExtensionProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionProvider.swift; sourceTree = "<group>"; }; + 3A096F8B2A4ED3DE00D4A2ED /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; + 3A096F8C2A4ED3DE00D4A2ED /* Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Extension.entitlements; sourceTree = "<group>"; }; + 3A0C6D3B2A79D46500A4DF2B /* OverviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverviewView.swift; sourceTree = "<group>"; }; + 3A0C6D3D2A79D6A600A4DF2B /* DashboardPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardPage.swift; sourceTree = "<group>"; }; + 3A172D2A2B88E9DB00D98050 /* BackButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackButton.swift; sourceTree = "<group>"; }; + 3A1CF2EF2A50E5EE000A8289 /* GroupListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupListView.swift; sourceTree = "<group>"; }; + 3A1CF2F12A50E613000A8289 /* OutboundGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutboundGroup.swift; sourceTree = "<group>"; }; + 3A1CF2F32A50E937000A8289 /* SidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarView.swift; sourceTree = "<group>"; }; + 3A1CF2F52A50EE9C000A8289 /* GroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupView.swift; sourceTree = "<group>"; }; + 3A1CF2F72A50F0A5000A8289 /* GroupItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupItemView.swift; sourceTree = "<group>"; }; + 3A1CF2F92A50F0BD000A8289 /* OutboundGroupItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutboundGroupItem.swift; sourceTree = "<group>"; }; + 3A2223532A6E1B6700C50B23 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; + 3A2223552A6E1BDE00C50B23 /* Variant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Variant.swift; sourceTree = "<group>"; }; + 3A2223572A6E1CC700C50B23 /* MacApplication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacApplication.swift; sourceTree = "<group>"; }; + 3A2223592A6E212A00C50B23 /* SystemExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemExtension.swift; sourceTree = "<group>"; }; + 3A27D8FF2A89BE230031EBCC /* CommandClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandClient.swift; sourceTree = "<group>"; }; + 3A27D9012A89C6870031EBCC /* ExtensionEnvironments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionEnvironments.swift; sourceTree = "<group>"; }; + 3A2EAEEC2A6F4CBB00D00DE3 /* IndependentApplicationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndependentApplicationDelegate.swift; sourceTree = "<group>"; }; + 3A3AB2A62B70C146001815AE /* CoreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreView.swift; sourceTree = "<group>"; }; + 3A3AB2A82B70C5F1001815AE /* RequestReviewButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestReviewButton.swift; sourceTree = "<group>"; }; + 3A3DEBE12A4FFA1A00373BF4 /* ExtensionFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ExtensionFoundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.0.sdk/System/Library/Frameworks/ExtensionFoundation.framework; sourceTree = DEVELOPER_DIR; }; + 3A3DEBE62A4FFA6000373BF4 /* AppIntents.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppIntents.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.0.sdk/System/Library/Frameworks/AppIntents.framework; sourceTree = DEVELOPER_DIR; }; + 3A411CEB2B734959000D9501 /* MacAppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacAppView.swift; sourceTree = "<group>"; }; + 3A44BB662A4DBF7900E4C9F8 /* SFI.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SFI.entitlements; sourceTree = "<group>"; }; + 3A44BB802A4DC25E00E4C9F8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; }; + 3A44BB812A4DC28700E4C9F8 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; }; + 3A4EAD102A4FEAE6005435B3 /* ApplicationLibrary.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ApplicationLibrary.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 3A4EAD202A4FEB3C005435B3 /* ApplicationLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationLibrary.swift; sourceTree = "<group>"; }; + 3A4EAD3B2A4FECCE005435B3 /* NEVPNStatus+isConnected.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NEVPNStatus+isConnected.swift"; sourceTree = "<group>"; }; + 3A4F68AF2A97602C003D66D3 /* ClashModeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashModeView.swift; sourceTree = "<group>"; }; + 3A4FB1642A73568E007012B9 /* SFT.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SFT.entitlements; sourceTree = "<group>"; }; + 3A4FB1652A73574B007012B9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; + 3A4FB1672A7358C9007012B9 /* ApplicationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationDelegate.swift; sourceTree = "<group>"; }; + 3A4FB1692A735AC9007012B9 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; }; + 3A55F9572A4D137E003C4EF4 /* UIProfileUpdateTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIProfileUpdateTask.swift; sourceTree = "<group>"; }; + 3A55F9592A4D1554003C4EF4 /* ProfileUpdateTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileUpdateTask.swift; sourceTree = "<group>"; }; + 3A57DF362A4D5D2600690BC5 /* Profile+Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Profile+Date.swift"; sourceTree = "<group>"; }; + 3A57DF3F2A4D70B600690BC5 /* MenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuView.swift; sourceTree = "<group>"; }; + 3A57DF412A4D927A00690BC5 /* Profile+Hashable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Profile+Hashable.swift"; sourceTree = "<group>"; }; + 3A60CC262B70880100D2D682 /* PacketTunnelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelView.swift; sourceTree = "<group>"; }; + 3A60CC282B70A7C400D2D682 /* Color+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Extension.swift"; sourceTree = "<group>"; }; + 3A60CC2A2B70AD6700D2D682 /* SettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingView.swift; sourceTree = "<group>"; }; + 3A648D2C2A4EEAA600D95A12 /* Library.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Library.swift; sourceTree = "<group>"; }; + 3A6CA4532BC19FDE0012B238 /* OnDemandRulesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnDemandRulesView.swift; sourceTree = "<group>"; }; + 3A6CA5A52A713AA10027933B /* AppIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = AppIcon.icns; sourceTree = "<group>"; }; + 3A77016D2A4E6B34008F031F /* IntentsExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.extensionkit-extension"; includeInIndex = 0; path = IntentsExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 3A77016F2A4E6B34008F031F /* IntentsExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentsExtension.swift; sourceTree = "<group>"; }; + 3A7701712A4E6B34008F031F /* Intents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Intents.swift; sourceTree = "<group>"; }; + 3A7701732A4E6B34008F031F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; + 3A7701802A4E71F5008F031F /* IntentsExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = IntentsExtension.entitlements; sourceTree = "<group>"; }; + 3A79044F2B6E7BAC006C08D5 /* SponsorsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SponsorsView.swift; sourceTree = "<group>"; }; + 3A7E90302A46745A00D53052 /* ViewBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewBuilder.swift; sourceTree = "<group>"; }; + 3A7E90342A46756300D53052 /* SharedPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedPreferences.swift; sourceTree = "<group>"; }; + 3A9144D82A46AE370036E9AD /* ShadredPreferences+Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ShadredPreferences+Database.swift"; sourceTree = "<group>"; }; + 3A99B4292A7526990010D4B0 /* NavigationStackCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationStackCompat.swift; sourceTree = "<group>"; }; + 3A99B42B2A75288C0010D4B0 /* ViewCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewCompat.swift; sourceTree = "<group>"; }; + 3A99B42D2A752ABB0010D4B0 /* NavigationDestinationCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationDestinationCompat.swift; sourceTree = "<group>"; }; + 3AA1ABB92A4C4054000FD4BA /* LogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogView.swift; sourceTree = "<group>"; }; + 3AAB5E732A4BF90B009757F1 /* ServiceLogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceLogView.swift; sourceTree = "<group>"; }; + 3AAB5E752A4BFB0B009757F1 /* EditProfileContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileContentView.swift; sourceTree = "<group>"; }; + 3AAB5E7A2A4C1446009757F1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; + 3AABFD422A9CC5A7005A24A4 /* Upload.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Upload.plist; sourceTree = "<group>"; }; + 3AABFD462A9CCC58005A24A4 /* Upload.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Upload.plist; sourceTree = "<group>"; }; + 3AB1220A2A70FD500087CD55 /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; }; + 3ABA46D22A6A32A100D8366B /* Messages.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Messages.framework; path = Library/Frameworks/Messages.framework; sourceTree = DEVELOPER_DIR; }; + 3AC03B982A72BF3300B7946F /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; }; + 3AC03B9C2A72BF3500B7946F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; + 3AC1944E2A50247300BD8CB9 /* ApplicationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationDelegate.swift; sourceTree = "<group>"; }; + 3AC194512A50303300BD8CB9 /* ApplicationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationDelegate.swift; sourceTree = "<group>"; }; + 3AC5EC072A6417470077AF34 /* DeviceCensorship.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceCensorship.swift; sourceTree = "<group>"; }; + 3AC729EF2A75D9D000FE8EC1 /* Profile+Transferable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Profile+Transferable.swift"; sourceTree = "<group>"; }; + 3AC729F12A76088E00FE8EC1 /* ShareButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareButton.swift; sourceTree = "<group>"; }; + 3AC8CF9A2A736C750002AF3C /* ImportProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportProfileView.swift; sourceTree = "<group>"; }; + 3ACA8B322B7E037800B7238F /* DeleteButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteButton.swift; sourceTree = "<group>"; }; + 3ACE6DE22ACADF55009D9A8A /* Binding+Setter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+Setter.swift"; sourceTree = "<group>"; }; + 3AD0953C2A70EB310052764E /* Profile+Share.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Profile+Share.swift"; sourceTree = "<group>"; }; + 3ADBB4242A7389640041D44F /* ProfileServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileServer.swift; sourceTree = "<group>"; }; + 3ADBB4292A73A7060041D44F /* NWSocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NWSocket.swift; sourceTree = "<group>"; }; + 3ADF8DF12A4AF59900900CC8 /* ActiveDashboardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveDashboardView.swift; sourceTree = "<group>"; }; + 3ADF8DF32A4AF9B500900CC8 /* DashboardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardView.swift; sourceTree = "<group>"; }; + 3ADF8DF62A4AFB2C00900CC8 /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = "<group>"; }; + 3ADF8DF82A4AFCB400900CC8 /* NewProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewProfileView.swift; sourceTree = "<group>"; }; + 3ADF8E002A4B0F6300900CC8 /* EditProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileView.swift; sourceTree = "<group>"; }; + 3ADF8E022A4B118700900CC8 /* Binding+Unwrap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+Unwrap.swift"; sourceTree = "<group>"; }; + 3AE1719C2A8128DD00393060 /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = "<group>"; }; + 3AE1719E2A8128DD00393060 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; + 3AE1719F2A8128DD00393060 /* TVExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = TVExtension.entitlements; sourceTree = "<group>"; }; + 3AE4D0B62A6E2C01009FEA9E /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = "<group>"; }; + 3AE4D0C02A6E4852009FEA9E /* InstallSystemExtensionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallSystemExtensionButton.swift; sourceTree = "<group>"; }; + 3AEAEE9C2A4F1A9D0059612D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; + 3AEC20DB2A4599D000A63465 /* Libbox.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Libbox.xcframework; sourceTree = "<group>"; }; + 3AEC20F32A459AB400A63465 /* sing-box.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "sing-box.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3AEC20F52A459AB400A63465 /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; }; + 3AEC20F92A459AB500A63465 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; + 3AEC210B2A459B1900A63465 /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; }; + 3AEC210D2A459B1900A63465 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; }; + 3AEC21142A459B1A00A63465 /* SFM.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SFM.entitlements; sourceTree = "<group>"; }; + 3AEC211D2A459B4700A63465 /* Library.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Library.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 3AEC212E2A459D5600A63465 /* Profile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Profile.swift; sourceTree = "<group>"; }; + 3AEC213B2A459FDF00A63465 /* Databse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Databse.swift; sourceTree = "<group>"; }; + 3AEC213F2A45A28F00A63465 /* ProfileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileManager.swift; sourceTree = "<group>"; }; + 3AEC21412A45A8FF00A63465 /* Profile+Update.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Profile+Update.swift"; sourceTree = "<group>"; }; + 3AEC21442A45A93800A63465 /* HTTPClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPClient.swift; sourceTree = "<group>"; }; + 3AEC21472A45A9DE00A63465 /* Bundle+Version.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Version.swift"; sourceTree = "<group>"; }; + 3AEC21492A45AA5600A63465 /* Profile+RW.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Profile+RW.swift"; sourceTree = "<group>"; }; + 3AEC214B2A45AA8E00A63465 /* FilePath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePath.swift; sourceTree = "<group>"; }; + 3AEC21742A45B0B800A63465 /* NavigationPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationPage.swift; sourceTree = "<group>"; }; + 3AEC21782A45BA5300A63465 /* EnvironmentValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentValues.swift; sourceTree = "<group>"; }; + 3AEECBF42A6DF40A006A0E0C /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = "<group>"; }; + 3AEECBF62A6DF40A006A0E0C /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; }; + 3AEECBF82A6DF40A006A0E0C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; + 3AEECBF92A6DF40A006A0E0C /* SystemExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SystemExtension.entitlements; sourceTree = "<group>"; }; + 3AEECC062A6DF9CA006A0E0C /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; }; + 3AEECC0A2A6DF9CA006A0E0C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; + 3AEECC3E2A6DFDF7006A0E0C /* MacLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacLibrary.swift; sourceTree = "<group>"; }; + 3AEECC502A6E0074006A0E0C /* SFM.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = SFM.entitlements; sourceTree = "<group>"; }; + 3AF3429F2A4A9916002B34AC /* ExtensionProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionProfile.swift; sourceTree = "<group>"; }; + 3AF342A22A4A9B9B002B34AC /* InstallProfileButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallProfileButton.swift; sourceTree = "<group>"; }; + 3AF342A62A4AA0FF002B34AC /* ExtensionPlatformInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionPlatformInterface.swift; sourceTree = "<group>"; }; + 3AF342A82A4AA155002B34AC /* Extension+RunBlocking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Extension+RunBlocking.swift"; sourceTree = "<group>"; }; + 3AF342AA2A4AA173002B34AC /* Extension+Iterator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Extension+Iterator.swift"; sourceTree = "<group>"; }; + 3AF342B12A4AA520002B34AC /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; }; + 3AF342CC2A4AA88C002B34AC /* StartStopButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartStopButton.swift; sourceTree = "<group>"; }; + 3AF342D02A4AACC4002B34AC /* ExtensionStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionStatusView.swift; sourceTree = "<group>"; }; + 3AF342D32A4AADB2002B34AC /* FormItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormItem.swift; sourceTree = "<group>"; }; + 3AF3A3D12B2207E1001FD7C1 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS17.0.sdk/usr/lib/libresolv.tbd; sourceTree = DEVELOPER_DIR; }; + 3AF5E3E62B6F90640058B9E8 /* ProfileOverrideView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileOverrideView.swift; sourceTree = "<group>"; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 3A096F832A4ED3DE00D4A2ED /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3A3AA7FF2A4EFDB3002F78AB /* Library.framework in Frameworks */, + 3A648D542A4EF4C700D95A12 /* NetworkExtension.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3A4EAD0D2A4FEAE6005435B3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3A4EAD1B2A4FEB02005435B3 /* Library.framework in Frameworks */, + 3A4A020D2B53E3DC004EFB87 /* QRCode in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3A77016A2A4E6B34008F031F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3A3DEBEB2A4FFE2D00373BF4 /* AppIntents.framework in Frameworks */, + 3A3AA7FC2A4EFDAE002F78AB /* Library.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3AEC20F02A459AB400A63465 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3A9759202A4EB69C00E4404B /* Library.framework in Frameworks */, + 3A4EAD372A4FEC20005435B3 /* ApplicationLibrary.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3AEC211A2A459B4700A63465 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3A017F922A4AB2E4009149FA /* GRDB in Frameworks */, + 3A76504C2A4F08BA003945C5 /* Libbox.xcframework in Frameworks */, + 3A7E90382A46778E00D53052 /* BinaryCodable in Frameworks */, + 3AF3A3D22B2207F3001FD7C1 /* libresolv.tbd in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 3A096F882A4ED3DE00D4A2ED /* Extension */ = { + isa = PBXGroup; + children = ( + 3A096F8B2A4ED3DE00D4A2ED /* Info.plist */, + 3A096F8C2A4ED3DE00D4A2ED /* Extension.entitlements */, + 3AE4D0B62A6E2C01009FEA9E /* PacketTunnelProvider.swift */, + ); + path = Extension; + sourceTree = "<group>"; + }; + 3A1CF2EE2A50E5D5000A8289 /* Groups */ = { + isa = PBXGroup; + children = ( + 3A1CF2F92A50F0BD000A8289 /* OutboundGroupItem.swift */, + 3A1CF2EF2A50E5EE000A8289 /* GroupListView.swift */, + 3A1CF2F12A50E613000A8289 /* OutboundGroup.swift */, + 3A1CF2F52A50EE9C000A8289 /* GroupView.swift */, + 3A1CF2F72A50F0A5000A8289 /* GroupItemView.swift */, + ); + path = Groups; + sourceTree = "<group>"; + }; + 3A4EAD112A4FEAE6005435B3 /* ApplicationLibrary */ = { + isa = PBXGroup; + children = ( + 3AAB5E7A2A4C1446009757F1 /* Assets.xcassets */, + 3A55F9562A4D1366003C4EF4 /* Service */, + 3AEC21732A45B0AC00A63465 /* Views */, + 3A4EAD202A4FEB3C005435B3 /* ApplicationLibrary.swift */, + ); + path = ApplicationLibrary; + sourceTree = "<group>"; + }; + 3A55F9562A4D1366003C4EF4 /* Service */ = { + isa = PBXGroup; + children = ( + 3A55F9572A4D137E003C4EF4 /* UIProfileUpdateTask.swift */, + 3A55F9592A4D1554003C4EF4 /* ProfileUpdateTask.swift */, + ); + path = Service; + sourceTree = "<group>"; + }; + 3A6CA5A42A713A6C0027933B /* Icons */ = { + isa = PBXGroup; + children = ( + 3A6CA5A52A713AA10027933B /* AppIcon.icns */, + ); + path = Icons; + sourceTree = "<group>"; + }; + 3A77016E2A4E6B34008F031F /* IntentsExtension */ = { + isa = PBXGroup; + children = ( + 3A7701802A4E71F5008F031F /* IntentsExtension.entitlements */, + 3A77016F2A4E6B34008F031F /* IntentsExtension.swift */, + 3A7701712A4E6B34008F031F /* Intents.swift */, + 3A7701732A4E6B34008F031F /* Info.plist */, + ); + path = IntentsExtension; + sourceTree = "<group>"; + }; + 3AA1ABB62A4C401A000FD4BA /* Log */ = { + isa = PBXGroup; + children = ( + 3AA1ABB92A4C4054000FD4BA /* LogView.swift */, + ); + path = Log; + sourceTree = "<group>"; + }; + 3AAB5E702A4BF6EA009757F1 /* Setting */ = { + isa = PBXGroup; + children = ( + 3AAB5E732A4BF90B009757F1 /* ServiceLogView.swift */, + 3A79044F2B6E7BAC006C08D5 /* SponsorsView.swift */, + 3AF5E3E62B6F90640058B9E8 /* ProfileOverrideView.swift */, + 3A60CC262B70880100D2D682 /* PacketTunnelView.swift */, + 3A60CC2A2B70AD6700D2D682 /* SettingView.swift */, + 3A3AB2A62B70C146001815AE /* CoreView.swift */, + 3A411CEB2B734959000D9501 /* MacAppView.swift */, + 3A6CA4532BC19FDE0012B238 /* OnDemandRulesView.swift */, + ); + path = Setting; + sourceTree = "<group>"; + }; + 3AC03B972A72BF3300B7946F /* SFT */ = { + isa = PBXGroup; + children = ( + 3A4FB1642A73568E007012B9 /* SFT.entitlements */, + 3AC03B982A72BF3300B7946F /* Application.swift */, + 3AC03B9C2A72BF3500B7946F /* Assets.xcassets */, + 3A4FB1652A73574B007012B9 /* Info.plist */, + 3A4FB1672A7358C9007012B9 /* ApplicationDelegate.swift */, + 3A4FB1692A735AC9007012B9 /* MainView.swift */, + ); + path = SFT; + sourceTree = "<group>"; + }; + 3ADBB4262A739DD00041D44F /* Discovery */ = { + isa = PBXGroup; + children = ( + 3ADBB4242A7389640041D44F /* ProfileServer.swift */, + 3ADBB4292A73A7060041D44F /* NWSocket.swift */, + ); + path = Discovery; + sourceTree = "<group>"; + }; + 3ADF8DF52A4AFA8E00900CC8 /* Profile */ = { + isa = PBXGroup; + children = ( + 3AAB5E752A4BFB0B009757F1 /* EditProfileContentView.swift */, + 3ADF8DF62A4AFB2C00900CC8 /* ProfileView.swift */, + 3ADF8DF82A4AFCB400900CC8 /* NewProfileView.swift */, + 3ADF8E002A4B0F6300900CC8 /* EditProfileView.swift */, + 3AC8CF9A2A736C750002AF3C /* ImportProfileView.swift */, + ); + path = Profile; + sourceTree = "<group>"; + }; + 3AE1719B2A8128DD00393060 /* TVExtension */ = { + isa = PBXGroup; + children = ( + 3AE1719C2A8128DD00393060 /* PacketTunnelProvider.swift */, + 3AE1719E2A8128DD00393060 /* Info.plist */, + 3AE1719F2A8128DD00393060 /* TVExtension.entitlements */, + ); + path = TVExtension; + sourceTree = "<group>"; + }; + 3AEC20BC2A45991900A63465 = { + isa = PBXGroup; + children = ( + 3AEC20DB2A4599D000A63465 /* Libbox.xcframework */, + 3AEC20F42A459AB400A63465 /* SFI */, + 3AEC210A2A459B1900A63465 /* SFM */, + 3AEECC052A6DF9CA006A0E0C /* SFM.System */, + 3AC03B972A72BF3300B7946F /* SFT */, + 3AEC211E2A459B4700A63465 /* Library */, + 3A4EAD112A4FEAE6005435B3 /* ApplicationLibrary */, + 3AEECC302A6DFDAD006A0E0C /* MacLibrary */, + 3A096F882A4ED3DE00D4A2ED /* Extension */, + 3AEECBF32A6DF40A006A0E0C /* SystemExtension */, + 3A77016E2A4E6B34008F031F /* IntentsExtension */, + 3AE1719B2A8128DD00393060 /* TVExtension */, + 3AEC20C72A45991900A63465 /* Products */, + 3AEC21012A459AE300A63465 /* Frameworks */, + ); + sourceTree = "<group>"; + }; + 3AEC20C72A45991900A63465 /* Products */ = { + isa = PBXGroup; + children = ( + 3AEC20F32A459AB400A63465 /* sing-box.app */, + 3AEC211D2A459B4700A63465 /* Library.framework */, + 3A77016D2A4E6B34008F031F /* IntentsExtension.appex */, + 3A096F862A4ED3DE00D4A2ED /* Extension.appex */, + 3A4EAD102A4FEAE6005435B3 /* ApplicationLibrary.framework */, + ); + name = Products; + sourceTree = "<group>"; + }; + 3AEC20F42A459AB400A63465 /* SFI */ = { + isa = PBXGroup; + children = ( + 3AABFD422A9CC5A7005A24A4 /* Upload.plist */, + 3A44BB802A4DC25E00E4C9F8 /* Info.plist */, + 3A44BB662A4DBF7900E4C9F8 /* SFI.entitlements */, + 3AEC20F52A459AB400A63465 /* Application.swift */, + 3AEC20F92A459AB500A63465 /* Assets.xcassets */, + 3A44BB812A4DC28700E4C9F8 /* MainView.swift */, + 3AC1944E2A50247300BD8CB9 /* ApplicationDelegate.swift */, + ); + path = SFI; + sourceTree = "<group>"; + }; + 3AEC21012A459AE300A63465 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 3AF3A3D12B2207E1001FD7C1 /* libresolv.tbd */, + 3A3DEBE62A4FFA6000373BF4 /* AppIntents.framework */, + 3A3DEBE12A4FFA1A00373BF4 /* ExtensionFoundation.framework */, + 3AF342B12A4AA520002B34AC /* NetworkExtension.framework */, + 3ABA46D22A6A32A100D8366B /* Messages.framework */, + ); + name = Frameworks; + sourceTree = "<group>"; + }; + 3AEC210A2A459B1900A63465 /* SFM */ = { + isa = PBXGroup; + children = ( + 3AEC210B2A459B1900A63465 /* Application.swift */, + 3AEC21142A459B1A00A63465 /* SFM.entitlements */, + 3AEAEE9C2A4F1A9D0059612D /* Info.plist */, + ); + path = SFM; + sourceTree = "<group>"; + }; + 3AEC211E2A459B4700A63465 /* Library */ = { + isa = PBXGroup; + children = ( + 3ADBB4262A739DD00041D44F /* Discovery */, + 3AEC21462A45A9CE00A63465 /* Shared */, + 3AEC21432A45A92B00A63465 /* Network */, + 3AEC213A2A459FD200A63465 /* Database */, + 3A648D2C2A4EEAA600D95A12 /* Library.swift */, + ); + path = Library; + sourceTree = "<group>"; + }; + 3AEC213A2A459FD200A63465 /* Database */ = { + isa = PBXGroup; + children = ( + 3AEC212E2A459D5600A63465 /* Profile.swift */, + 3AEC213B2A459FDF00A63465 /* Databse.swift */, + 3AEC213F2A45A28F00A63465 /* ProfileManager.swift */, + 3AEC21412A45A8FF00A63465 /* Profile+Update.swift */, + 3AEC21492A45AA5600A63465 /* Profile+RW.swift */, + 3A7E90342A46756300D53052 /* SharedPreferences.swift */, + 3A9144D82A46AE370036E9AD /* ShadredPreferences+Database.swift */, + 3A57DF362A4D5D2600690BC5 /* Profile+Date.swift */, + 3A57DF412A4D927A00690BC5 /* Profile+Hashable.swift */, + 3AD0953C2A70EB310052764E /* Profile+Share.swift */, + 3AC729EF2A75D9D000FE8EC1 /* Profile+Transferable.swift */, + ); + path = Database; + sourceTree = "<group>"; + }; + 3AEC21432A45A92B00A63465 /* Network */ = { + isa = PBXGroup; + children = ( + 3A096F892A4ED3DE00D4A2ED /* ExtensionProvider.swift */, + 3AF342AA2A4AA173002B34AC /* Extension+Iterator.swift */, + 3AF342A82A4AA155002B34AC /* Extension+RunBlocking.swift */, + 3AEC21442A45A93800A63465 /* HTTPClient.swift */, + 3AF3429F2A4A9916002B34AC /* ExtensionProfile.swift */, + 3A4EAD3B2A4FECCE005435B3 /* NEVPNStatus+isConnected.swift */, + 3A2223592A6E212A00C50B23 /* SystemExtension.swift */, + 3AF342A62A4AA0FF002B34AC /* ExtensionPlatformInterface.swift */, + 3A27D8FF2A89BE230031EBCC /* CommandClient.swift */, + 3A27D9012A89C6870031EBCC /* ExtensionEnvironments.swift */, + ); + path = Network; + sourceTree = "<group>"; + }; + 3AEC21462A45A9CE00A63465 /* Shared */ = { + isa = PBXGroup; + children = ( + 3AEC21472A45A9DE00A63465 /* Bundle+Version.swift */, + 3AEC214B2A45AA8E00A63465 /* FilePath.swift */, + 3A2223552A6E1BDE00C50B23 /* Variant.swift */, + 3A60CC282B70A7C400D2D682 /* Color+Extension.swift */, + ); + path = Shared; + sourceTree = "<group>"; + }; + 3AEC21732A45B0AC00A63465 /* Views */ = { + isa = PBXGroup; + children = ( + 3AF342D22A4AADA5002B34AC /* Abstract */, + 3AF342A12A4A9B8D002B34AC /* Dashboard */, + 3A1CF2EE2A50E5D5000A8289 /* Groups */, + 3AA1ABB62A4C401A000FD4BA /* Log */, + 3ADF8DF52A4AFA8E00900CC8 /* Profile */, + 3AAB5E702A4BF6EA009757F1 /* Setting */, + 3AEC21742A45B0B800A63465 /* NavigationPage.swift */, + 3AEC21782A45BA5300A63465 /* EnvironmentValues.swift */, + ); + path = Views; + sourceTree = "<group>"; + }; + 3AEECBF32A6DF40A006A0E0C /* SystemExtension */ = { + isa = PBXGroup; + children = ( + 3AEECBF42A6DF40A006A0E0C /* PacketTunnelProvider.swift */, + 3AEECBF62A6DF40A006A0E0C /* main.swift */, + 3AEECBF82A6DF40A006A0E0C /* Info.plist */, + 3AEECBF92A6DF40A006A0E0C /* SystemExtension.entitlements */, + ); + path = SystemExtension; + sourceTree = "<group>"; + }; + 3AEECC052A6DF9CA006A0E0C /* SFM.System */ = { + isa = PBXGroup; + children = ( + 3AEECC062A6DF9CA006A0E0C /* Application.swift */, + 3AEECC502A6E0074006A0E0C /* SFM.entitlements */, + 3A2223532A6E1B6700C50B23 /* Info.plist */, + 3A2EAEEC2A6F4CBB00D00DE3 /* IndependentApplicationDelegate.swift */, + 3AABFD462A9CCC58005A24A4 /* Upload.plist */, + ); + path = SFM.System; + sourceTree = "<group>"; + }; + 3AEECC302A6DFDAD006A0E0C /* MacLibrary */ = { + isa = PBXGroup; + children = ( + 3A6CA5A42A713A6C0027933B /* Icons */, + 3AEECC0A2A6DF9CA006A0E0C /* Assets.xcassets */, + 3AEC210D2A459B1900A63465 /* MainView.swift */, + 3A57DF3F2A4D70B600690BC5 /* MenuView.swift */, + 3A1CF2F32A50E937000A8289 /* SidebarView.swift */, + 3AEECC3E2A6DFDF7006A0E0C /* MacLibrary.swift */, + 3AC194512A50303300BD8CB9 /* ApplicationDelegate.swift */, + 3A2223572A6E1CC700C50B23 /* MacApplication.swift */, + ); + path = MacLibrary; + sourceTree = "<group>"; + }; + 3AF342A12A4A9B8D002B34AC /* Dashboard */ = { + isa = PBXGroup; + children = ( + 3AF342A22A4A9B9B002B34AC /* InstallProfileButton.swift */, + 3AF342D02A4AACC4002B34AC /* ExtensionStatusView.swift */, + 3ADF8DF12A4AF59900900CC8 /* ActiveDashboardView.swift */, + 3AF342CC2A4AA88C002B34AC /* StartStopButton.swift */, + 3ADF8DF32A4AF9B500900CC8 /* DashboardView.swift */, + 3AE4D0C02A6E4852009FEA9E /* InstallSystemExtensionButton.swift */, + 3A0C6D3B2A79D46500A4DF2B /* OverviewView.swift */, + 3A0C6D3D2A79D6A600A4DF2B /* DashboardPage.swift */, + 3A4F68AF2A97602C003D66D3 /* ClashModeView.swift */, + ); + path = Dashboard; + sourceTree = "<group>"; + }; + 3AF342D22A4AADA5002B34AC /* Abstract */ = { + isa = PBXGroup; + children = ( + 3A7E90302A46745A00D53052 /* ViewBuilder.swift */, + 3AF342D32A4AADB2002B34AC /* FormItem.swift */, + 3ADF8E022A4B118700900CC8 /* Binding+Unwrap.swift */, + 3AC5EC072A6417470077AF34 /* DeviceCensorship.swift */, + 3AB1220A2A70FD500087CD55 /* Alert.swift */, + 3A99B4292A7526990010D4B0 /* NavigationStackCompat.swift */, + 3A99B42B2A75288C0010D4B0 /* ViewCompat.swift */, + 3A99B42D2A752ABB0010D4B0 /* NavigationDestinationCompat.swift */, + 3AC729F12A76088E00FE8EC1 /* ShareButton.swift */, + 3ACE6DE22ACADF55009D9A8A /* Binding+Setter.swift */, + 3A3AB2A82B70C5F1001815AE /* RequestReviewButton.swift */, + 3ACA8B322B7E037800B7238F /* DeleteButton.swift */, + 3A172D2A2B88E9DB00D98050 /* BackButton.swift */, + ); + path = Abstract; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 3A4EAD0B2A4FEAE6005435B3 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3AEC21182A459B4700A63465 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 3A096F852A4ED3DE00D4A2ED /* Extension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3A096F902A4ED3DE00D4A2ED /* Build configuration list for PBXNativeTarget "Extension" */; + buildPhases = ( + 3A096F822A4ED3DE00D4A2ED /* Sources */, + 3A096F832A4ED3DE00D4A2ED /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 3A76504B2A4F07F6003945C5 /* PBXTargetDependency */, + ); + name = Extension; + productName = Extension; + productReference = 3A096F862A4ED3DE00D4A2ED /* Extension.appex */; + productType = "com.apple.product-type.app-extension"; + }; + 3A4EAD0F2A4FEAE6005435B3 /* ApplicationLibrary */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3A4EAD182A4FEAE6005435B3 /* Build configuration list for PBXNativeTarget "ApplicationLibrary" */; + buildPhases = ( + 3A4EAD0B2A4FEAE6005435B3 /* Headers */, + 3A4EAD0C2A4FEAE6005435B3 /* Sources */, + 3A4EAD0D2A4FEAE6005435B3 /* Frameworks */, + 3A4EAD0E2A4FEAE6005435B3 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 3A4EAD1E2A4FEB02005435B3 /* PBXTargetDependency */, + ); + name = ApplicationLibrary; + packageProductDependencies = ( + 3A4A020C2B53E3DC004EFB87 /* QRCode */, + ); + productName = ApplicationLibrary; + productReference = 3A4EAD102A4FEAE6005435B3 /* ApplicationLibrary.framework */; + productType = "com.apple.product-type.framework"; + }; + 3A77016C2A4E6B34008F031F /* IntentsExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3A7701772A4E6B34008F031F /* Build configuration list for PBXNativeTarget "IntentsExtension" */; + buildPhases = ( + 3A7701692A4E6B34008F031F /* Sources */, + 3A77016A2A4E6B34008F031F /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 3A77017E2A4E6B5E008F031F /* PBXTargetDependency */, + ); + name = IntentsExtension; + productName = IntentsExtension; + productReference = 3A77016D2A4E6B34008F031F /* IntentsExtension.appex */; + productType = "com.apple.product-type.extensionkit-extension"; + }; + 3AEC20F22A459AB400A63465 /* SFI */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3AEC20FE2A459AB500A63465 /* Build configuration list for PBXNativeTarget "SFI" */; + buildPhases = ( + 3AEC20EF2A459AB400A63465 /* Sources */, + 3AEC20F02A459AB400A63465 /* Frameworks */, + 3AEC20F12A459AB400A63465 /* Resources */, + 3A44BB782A4DC17000E4C9F8 /* Embed Foundation Extensions */, + 3A9759222A4EB69C00E4404B /* Embed Frameworks */, + 3A8655172A4FA26600B7181F /* Embed ExtensionKit Extensions */, + ); + buildRules = ( + ); + dependencies = ( + 3AEC21382A459E0A00A63465 /* PBXTargetDependency */, + 3A44BB7F2A4DC1D800E4C9F8 /* PBXTargetDependency */, + 3AEAEE9B2A4F16430059612D /* PBXTargetDependency */, + 3A8655162A4FA26600B7181F /* PBXTargetDependency */, + 3A4EAD3A2A4FEC20005435B3 /* PBXTargetDependency */, + ); + name = SFI; + packageProductDependencies = ( + ); + productName = SFI; + productReference = 3AEC20F32A459AB400A63465 /* sing-box.app */; + productType = "com.apple.product-type.application"; + }; + 3AEC211C2A459B4700A63465 /* Library */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3AEC21252A459B4700A63465 /* Build configuration list for PBXNativeTarget "Library" */; + buildPhases = ( + 3AEC21182A459B4700A63465 /* Headers */, + 3AEC21192A459B4700A63465 /* Sources */, + 3AEC211A2A459B4700A63465 /* Frameworks */, + 3AEC211B2A459B4700A63465 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Library; + packageProductDependencies = ( + 3A7E90372A46778E00D53052 /* BinaryCodable */, + 3A017F912A4AB2E4009149FA /* GRDB */, + ); + productName = Library; + productReference = 3AEC211D2A459B4700A63465 /* Library.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 3AEC20BD2A45991900A63465 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1500; + LastUpgradeCheck = 1430; + TargetAttributes = { + 3A096F852A4ED3DE00D4A2ED = { + CreatedOnToolsVersion = 15.0; + LastSwiftMigration = 1430; + }; + 3A4EAD0F2A4FEAE6005435B3 = { + CreatedOnToolsVersion = 15.0; + LastSwiftMigration = 1500; + }; + 3A77016C2A4E6B34008F031F = { + CreatedOnToolsVersion = 15.0; + }; + 3AEC20F22A459AB400A63465 = { + CreatedOnToolsVersion = 15.0; + }; + 3AEC211C2A459B4700A63465 = { + CreatedOnToolsVersion = 15.0; + LastSwiftMigration = 1500; + }; + }; + }; + buildConfigurationList = 3AEC20C02A45991900A63465 /* Build configuration list for PBXProject "sing-box" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + "zh-Hans", + ); + mainGroup = 3AEC20BC2A45991900A63465; + packageReferences = ( + 3A7E90362A46778E00D53052 /* XCRemoteSwiftPackageReference "BinaryCodable" */, + 3A017F902A4AB2E4009149FA /* XCRemoteSwiftPackageReference "GRDB" */, + 3A57DF3A2A4D705000690BC5 /* XCRemoteSwiftPackageReference "MacControlCenterUI" */, + 3A4A020B2B53E3DC004EFB87 /* XCRemoteSwiftPackageReference "qrcode" */, + ); + productRefGroup = 3AEC20C72A45991900A63465 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 3AEC20F22A459AB400A63465 /* SFI */, + 3AEC211C2A459B4700A63465 /* Library */, + 3A4EAD0F2A4FEAE6005435B3 /* ApplicationLibrary */, + 3A096F852A4ED3DE00D4A2ED /* Extension */, + 3A77016C2A4E6B34008F031F /* IntentsExtension */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 3A4EAD0E2A4FEAE6005435B3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3AEC20F12A459AB400A63465 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3A6CA5A92A713C420027933B /* AppIcon.icns in Resources */, + 3AABFD432A9CC5A7005A24A4 /* Upload.plist in Resources */, + 3AEC20FA2A459AB500A63465 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3AEC211B2A459B4700A63465 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 3A096F822A4ED3DE00D4A2ED /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3AE4D0B72A6E2C01009FEA9E /* PacketTunnelProvider.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3A4EAD0C2A4FEAE6005435B3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3A60CC2B2B70AD6700D2D682 /* SettingView.swift in Sources */, + 3A99B42A2A7526990010D4B0 /* NavigationStackCompat.swift in Sources */, + 3A4EAD2E2A4FEB77005435B3 /* EditProfileView.swift in Sources */, + 3A4EAD2A2A4FEB6D005435B3 /* Binding+Unwrap.swift in Sources */, + 3A4EAD2D2A4FEB77005435B3 /* ProfileView.swift in Sources */, + 3A4EAD352A4FEB9C005435B3 /* UIProfileUpdateTask.swift in Sources */, + 3A60CC272B70880100D2D682 /* PacketTunnelView.swift in Sources */, + 3A4EAD222A4FEB54005435B3 /* NavigationPage.swift in Sources */, + 3A411CEC2B734959000D9501 /* MacAppView.swift in Sources */, + 3AC8CF9B2A736C750002AF3C /* ImportProfileView.swift in Sources */, + 3A4EAD292A4FEB6D005435B3 /* FormItem.swift in Sources */, + 3AF5E3E72B6F90640058B9E8 /* ProfileOverrideView.swift in Sources */, + 3A4EAD302A4FEB77005435B3 /* NewProfileView.swift in Sources */, + 3A3AB2A72B70C146001815AE /* CoreView.swift in Sources */, + 3A172D2B2B88E9DB00D98050 /* BackButton.swift in Sources */, + 3A4EAD242A4FEB65005435B3 /* InstallProfileButton.swift in Sources */, + 3AE4D0C12A6E4852009FEA9E /* InstallSystemExtensionButton.swift in Sources */, + 3A4EAD232A4FEB5A005435B3 /* EnvironmentValues.swift in Sources */, + 3A4F68B02A97602C003D66D3 /* ClashModeView.swift in Sources */, + 3A4EAD282A4FEB65005435B3 /* ActiveDashboardView.swift in Sources */, + 3A1CF2F02A50E5EE000A8289 /* GroupListView.swift in Sources */, + 3A4EAD322A4FEB7B005435B3 /* ServiceLogView.swift in Sources */, + 3A1CF2F82A50F0A5000A8289 /* GroupItemView.swift in Sources */, + 3A4EAD2F2A4FEB77005435B3 /* EditProfileContentView.swift in Sources */, + 3A4EAD272A4FEB65005435B3 /* DashboardView.swift in Sources */, + 3A4EAD342A4FEB7F005435B3 /* LogView.swift in Sources */, + 3A99B42C2A75288C0010D4B0 /* ViewCompat.swift in Sources */, + 3AB1220B2A70FD500087CD55 /* Alert.swift in Sources */, + 3A4EAD362A4FEB9C005435B3 /* ProfileUpdateTask.swift in Sources */, + 3A1CF2F62A50EE9C000A8289 /* GroupView.swift in Sources */, + 3A4EAD212A4FEB3C005435B3 /* ApplicationLibrary.swift in Sources */, + 3A7904502B6E7BAC006C08D5 /* SponsorsView.swift in Sources */, + 3A6CA4542BC19FDE0012B238 /* OnDemandRulesView.swift in Sources */, + 3A1CF2F22A50E613000A8289 /* OutboundGroup.swift in Sources */, + 3A1CF2FA2A50F0BD000A8289 /* OutboundGroupItem.swift in Sources */, + 3A99B42E2A752ABB0010D4B0 /* NavigationDestinationCompat.swift in Sources */, + 3AC729F22A76088E00FE8EC1 /* ShareButton.swift in Sources */, + 3A0C6D3C2A79D46500A4DF2B /* OverviewView.swift in Sources */, + 3ACA8B332B7E037800B7238F /* DeleteButton.swift in Sources */, + 3ACE6DE32ACADF55009D9A8A /* Binding+Setter.swift in Sources */, + 3A4EAD262A4FEB65005435B3 /* ExtensionStatusView.swift in Sources */, + 3A4EAD252A4FEB65005435B3 /* StartStopButton.swift in Sources */, + 3A4EAD2B2A4FEB6D005435B3 /* ViewBuilder.swift in Sources */, + 3AC5EC082A6417470077AF34 /* DeviceCensorship.swift in Sources */, + 3A0C6D3E2A79D6A600A4DF2B /* DashboardPage.swift in Sources */, + 3A3AB2A92B70C5F1001815AE /* RequestReviewButton.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3A7701692A4E6B34008F031F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3A7701702A4E6B34008F031F /* IntentsExtension.swift in Sources */, + 3A7701722A4E6B34008F031F /* Intents.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3AEC20EF2A459AB400A63465 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3AC1944F2A50247300BD8CB9 /* ApplicationDelegate.swift in Sources */, + 3AEC20F62A459AB400A63465 /* Application.swift in Sources */, + 3A44BB822A4DC28700E4C9F8 /* MainView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3AEC21192A459B4700A63465 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3A27D9022A89C6870031EBCC /* ExtensionEnvironments.swift in Sources */, + 3AE4D0B52A6E2BAC009FEA9E /* ExtensionProvider.swift in Sources */, + 3A2223562A6E1BDE00C50B23 /* Variant.swift in Sources */, + 3AEC214A2A45AA5600A63465 /* Profile+RW.swift in Sources */, + 3AC729F02A75D9D000FE8EC1 /* Profile+Transferable.swift in Sources */, + 3A57DF422A4D927A00690BC5 /* Profile+Hashable.swift in Sources */, + 3A7E90352A46756300D53052 /* SharedPreferences.swift in Sources */, + 3AD0953D2A70EB310052764E /* Profile+Share.swift in Sources */, + 3AE4D0B32A6E2B94009FEA9E /* Extension+RunBlocking.swift in Sources */, + 3AEC21482A45A9DE00A63465 /* Bundle+Version.swift in Sources */, + 3A57DF372A4D5D2600690BC5 /* Profile+Date.swift in Sources */, + 3AEC212F2A459D5600A63465 /* Profile.swift in Sources */, + 3A648D2D2A4EEAA600D95A12 /* Library.swift in Sources */, + 3AEC21452A45A93800A63465 /* HTTPClient.swift in Sources */, + 3AEC213C2A459FDF00A63465 /* Databse.swift in Sources */, + 3A60CC292B70A7C400D2D682 /* Color+Extension.swift in Sources */, + 3A4EAD3C2A4FECCE005435B3 /* NEVPNStatus+isConnected.swift in Sources */, + 3ADBB4252A7389640041D44F /* ProfileServer.swift in Sources */, + 3AEC21422A45A8FF00A63465 /* Profile+Update.swift in Sources */, + 3A22235A2A6E212A00C50B23 /* SystemExtension.swift in Sources */, + 3AE4D0B42A6E2BA3009FEA9E /* Extension+Iterator.swift in Sources */, + 3AEC214C2A45AA8E00A63465 /* FilePath.swift in Sources */, + 3ADBB42A2A73A7060041D44F /* NWSocket.swift in Sources */, + 3AE4D0B22A6E2B6A009FEA9E /* ExtensionPlatformInterface.swift in Sources */, + 3AEC21402A45A28F00A63465 /* ProfileManager.swift in Sources */, + 3A9144D92A46AE370036E9AD /* ShadredPreferences+Database.swift in Sources */, + 3A27D9002A89BE230031EBCC /* CommandClient.swift in Sources */, + 3AF342A02A4A9916002B34AC /* ExtensionProfile.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 3A44BB7F2A4DC1D800E4C9F8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3A096F852A4ED3DE00D4A2ED /* Extension */; + targetProxy = 3A44BB7E2A4DC1D800E4C9F8 /* PBXContainerItemProxy */; + }; + 3A4EAD1E2A4FEB02005435B3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3AEC211C2A459B4700A63465 /* Library */; + targetProxy = 3A4EAD1D2A4FEB02005435B3 /* PBXContainerItemProxy */; + }; + 3A4EAD3A2A4FEC20005435B3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3A4EAD0F2A4FEAE6005435B3 /* ApplicationLibrary */; + targetProxy = 3A4EAD392A4FEC20005435B3 /* PBXContainerItemProxy */; + }; + 3A76504B2A4F07F6003945C5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3AEC211C2A459B4700A63465 /* Library */; + targetProxy = 3A76504A2A4F07F6003945C5 /* PBXContainerItemProxy */; + }; + 3A77017E2A4E6B5E008F031F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3AEC211C2A459B4700A63465 /* Library */; + targetProxy = 3A77017D2A4E6B5E008F031F /* PBXContainerItemProxy */; + }; + 3A8655162A4FA26600B7181F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3A77016C2A4E6B34008F031F /* IntentsExtension */; + targetProxy = 3A8655152A4FA26600B7181F /* PBXContainerItemProxy */; + }; + 3AEAEE9B2A4F16430059612D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3A096F852A4ED3DE00D4A2ED /* Extension */; + targetProxy = 3AEAEE9A2A4F16430059612D /* PBXContainerItemProxy */; + }; + 3AEC21382A459E0A00A63465 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3AEC211C2A459B4700A63465 /* Library */; + targetProxy = 3AEC21372A459E0A00A63465 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 3A096F912A4ED3DE00D4A2ED /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Extension/Extension.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = Z7STA3KGEU; + ENABLE_HARDENED_RUNTIME = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Extension/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Extension; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INFOPLIST_KEY_UIRequiredDeviceCapabilities = arm64; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@executable_path/../../Frameworks", + "@executable_path/../../../../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 13.0; + MARKETING_VERSION = 1.0; + OTHER_CODE_SIGN_FLAGS = ""; + PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfabeino.extension; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 3A096F922A4ED3DE00D4A2ED /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Extension/Extension.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = Z7STA3KGEU; + ENABLE_HARDENED_RUNTIME = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Extension/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = Extension; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INFOPLIST_KEY_UIRequiredDeviceCapabilities = arm64; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@executable_path/../../Frameworks", + "@executable_path/../../../../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 13.0; + MARKETING_VERSION = 1.0; + OTHER_CODE_SIGN_FLAGS = ""; + PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfabeino.extension; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 3A4EAD192A4FEAE6005435B3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = Z7STA3KGEU; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACH_O_TYPE = staticlib; + MACOSX_DEPLOYMENT_TARGET = 13.0; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfabeino.application; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3"; + TVOS_DEPLOYMENT_TARGET = 17.0; + }; + name = Debug; + }; + 3A4EAD1A2A4FEAE6005435B3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = Z7STA3KGEU; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACH_O_TYPE = staticlib; + MACOSX_DEPLOYMENT_TARGET = 13.0; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfabeino.application; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3"; + TVOS_DEPLOYMENT_TARGET = 17.0; + }; + name = Release; + }; + 3A7701782A4E6B34008F031F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = IntentsExtension/IntentsExtension.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = Z7STA3KGEU; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = IntentsExtension/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = IntentsExtension; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + "@executable_path/../../../../Frameworks", + ); + LINK_WITH_STANDARD_LIBRARIES = YES; + MACOSX_DEPLOYMENT_TARGET = 13.0; + MARKETING_VERSION = 1.0; + OTHER_CODE_SIGN_FLAGS = ""; + PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfabeino.intents; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + REEXPORTED_LIBRARY_PATHS = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 3A7701792A4E6B34008F031F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = IntentsExtension/IntentsExtension.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = Z7STA3KGEU; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = IntentsExtension/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = IntentsExtension; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + "@executable_path/../../../../Frameworks", + ); + LINK_WITH_STANDARD_LIBRARIES = YES; + MACOSX_DEPLOYMENT_TARGET = 13.0; + MARKETING_VERSION = 1.0; + OTHER_CODE_SIGN_FLAGS = ""; + PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfabeino.intents; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + REEXPORTED_LIBRARY_PATHS = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 3AEC20CB2A45991900A63465 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 3AEC20CC2A45991900A63465 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = NO; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 3AEC20FF2A459AB500A63465 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + CODE_SIGN_ENTITLEMENTS = SFI/SFI.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = Z7STA3KGEU; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = SFI/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "sing-box"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportsDocumentBrowser = NO; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.9.4; + OTHER_CODE_SIGN_FLAGS = "--deep"; + PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfabeino; + PRODUCT_NAME = "sing-box"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 3AEC21002A459AB500A63465 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + CODE_SIGN_ENTITLEMENTS = SFI/SFI.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = Z7STA3KGEU; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = SFI/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "sing-box"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportsDocumentBrowser = NO; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.9.4; + OTHER_CODE_SIGN_FLAGS = "--deep"; + PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfabeino; + PRODUCT_NAME = "sing-box"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 3AEC21262A459B4700A63465 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = Z7STA3KGEU; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = NO; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_ON_DEMAND_RESOURCES = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACH_O_TYPE = mh_dylib; + MACOSX_DEPLOYMENT_TARGET = 13.0; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + OTHER_CODE_SIGN_FLAGS = "--deep"; + PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfabeino.library; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3"; + TVOS_DEPLOYMENT_TARGET = 17.0; + }; + name = Debug; + }; + 3AEC21272A459B4700A63465 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = Z7STA3KGEU; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE = NO; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_ON_DEMAND_RESOURCES = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACH_O_TYPE = mh_dylib; + MACOSX_DEPLOYMENT_TARGET = 13.0; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + OTHER_CODE_SIGN_FLAGS = "--deep"; + PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfabeino.library; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3"; + TVOS_DEPLOYMENT_TARGET = 17.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 3A096F902A4ED3DE00D4A2ED /* Build configuration list for PBXNativeTarget "Extension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3A096F912A4ED3DE00D4A2ED /* Debug */, + 3A096F922A4ED3DE00D4A2ED /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3A4EAD182A4FEAE6005435B3 /* Build configuration list for PBXNativeTarget "ApplicationLibrary" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3A4EAD192A4FEAE6005435B3 /* Debug */, + 3A4EAD1A2A4FEAE6005435B3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3A7701772A4E6B34008F031F /* Build configuration list for PBXNativeTarget "IntentsExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3A7701782A4E6B34008F031F /* Debug */, + 3A7701792A4E6B34008F031F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3AEC20C02A45991900A63465 /* Build configuration list for PBXProject "sing-box" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3AEC20CB2A45991900A63465 /* Debug */, + 3AEC20CC2A45991900A63465 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3AEC20FE2A459AB500A63465 /* Build configuration list for PBXNativeTarget "SFI" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3AEC20FF2A459AB500A63465 /* Debug */, + 3AEC21002A459AB500A63465 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3AEC21252A459B4700A63465 /* Build configuration list for PBXNativeTarget "Library" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3AEC21262A459B4700A63465 /* Debug */, + 3AEC21272A459B4700A63465 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 3A017F902A4AB2E4009149FA /* XCRemoteSwiftPackageReference "GRDB" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/groue/GRDB.swift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 6.15.1; + }; + }; + 3A4A020B2B53E3DC004EFB87 /* XCRemoteSwiftPackageReference "qrcode" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/dagronf/qrcode.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 17.0.0; + }; + }; + 3A57DF3A2A4D705000690BC5 /* XCRemoteSwiftPackageReference "MacControlCenterUI" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/orchetect/MacControlCenterUI"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.0.1; + }; + }; + 3A7E90362A46778E00D53052 /* XCRemoteSwiftPackageReference "BinaryCodable" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/christophhagen/BinaryCodable"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 3A017F912A4AB2E4009149FA /* GRDB */ = { + isa = XCSwiftPackageProductDependency; + package = 3A017F902A4AB2E4009149FA /* XCRemoteSwiftPackageReference "GRDB" */; + productName = GRDB; + }; + 3A4A020C2B53E3DC004EFB87 /* QRCode */ = { + isa = XCSwiftPackageProductDependency; + package = 3A4A020B2B53E3DC004EFB87 /* XCRemoteSwiftPackageReference "qrcode" */; + productName = QRCode; + }; + 3A7E90372A46778E00D53052 /* BinaryCodable */ = { + isa = XCSwiftPackageProductDependency; + package = 3A7E90362A46778E00D53052 /* XCRemoteSwiftPackageReference "BinaryCodable" */; + productName = BinaryCodable; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 3AEC20BD2A45991900A63465 /* Project object */; +} diff --git a/sing-box.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/sing-box.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000000000000000000000000000000000..919434a6254f0e9651f402737811be6634a03e9c --- /dev/null +++ b/sing-box.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Workspace + version = "1.0"> + <FileRef + location = "self:"> + </FileRef> +</Workspace> diff --git a/sing-box.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/sing-box.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000000000000000000000000000000000..18d981003d68d0546c4804ac2ff47dd97c6e7921 --- /dev/null +++ b/sing-box.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>IDEDidComputeMac32BitWarning</key> + <true/> +</dict> +</plist> diff --git a/sing-box.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/sing-box.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000000000000000000000000000000000000..6c91f1ef670e170865442214a63641d84db41ede --- /dev/null +++ b/sing-box.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,69 @@ +{ + "originHash" : "82fb479ec8c73c3348b93e33a9102fc692bb5a0a0d6f14beb4c5f31445c36e81", + "pins" : [ + { + "identity" : "binarycodable", + "kind" : "remoteSourceControl", + "location" : "https://github.com/christophhagen/BinaryCodable", + "state" : { + "revision" : "295ca6399b2b01d1aa4fa84d666416f3bf99ffde", + "version" : "2.0.0" + } + }, + { + "identity" : "grdb.swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/groue/GRDB.swift", + "state" : { + "revision" : "284a4ee1acf9bf6c8b3d045494d0a7e3046ebf92", + "version" : "6.15.1" + } + }, + { + "identity" : "maccontrolcenterui", + "kind" : "remoteSourceControl", + "location" : "https://github.com/orchetect/MacControlCenterUI", + "state" : { + "revision" : "e7f7e0834a146b59a9d86b5751b711eb3a57be69", + "version" : "2.0.1" + } + }, + { + "identity" : "menubarextraaccess", + "kind" : "remoteSourceControl", + "location" : "https://github.com/orchetect/MenuBarExtraAccess", + "state" : { + "revision" : "8757eb7c2cd708320df92e6ad6572efe90e58f16", + "version" : "1.0.4" + } + }, + { + "identity" : "qrcode", + "kind" : "remoteSourceControl", + "location" : "https://github.com/dagronf/qrcode.git", + "state" : { + "revision" : "13c605cfa866e257ac9d4ac8a47320a0e11fb6e0", + "version" : "17.0.0" + } + }, + { + "identity" : "swift-qrcode-generator", + "kind" : "remoteSourceControl", + "location" : "https://github.com/dagronf/swift-qrcode-generator", + "state" : { + "revision" : "2b1980b825f08a81ccc762b0c4d17fcde9d5e953", + "version" : "2.0.2" + } + }, + { + "identity" : "swiftimagereadwrite", + "kind" : "remoteSourceControl", + "location" : "https://github.com/dagronf/SwiftImageReadWrite", + "state" : { + "revision" : "02c141026a0c5d74635a457d2d0964c4cb8935b3", + "version" : "1.4.1" + } + } + ], + "version" : 3 +} diff --git a/sing-box.xcodeproj/project.xcworkspace/xcuserdata/g.makhoul.xcuserdatad/UserInterfaceState.xcuserstate b/sing-box.xcodeproj/project.xcworkspace/xcuserdata/g.makhoul.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..797330b62b6ae3e89002df236ff59cafe7e834c8 Binary files /dev/null and b/sing-box.xcodeproj/project.xcworkspace/xcuserdata/g.makhoul.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/sing-box.xcodeproj/project.xcworkspace/xcuserdata/mohammedarab.xcuserdatad/UserInterfaceState.xcuserstate b/sing-box.xcodeproj/project.xcworkspace/xcuserdata/mohammedarab.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..8e1832a125f91d1be70fdfd5c5728c8939c04b87 Binary files /dev/null and b/sing-box.xcodeproj/project.xcworkspace/xcuserdata/mohammedarab.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/sing-box.xcodeproj/xcshareddata/xcschemes/SFI.xcscheme b/sing-box.xcodeproj/xcshareddata/xcschemes/SFI.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..fb1b5839bc6c7dcdf0dc28bb851306ab24d9f340 --- /dev/null +++ b/sing-box.xcodeproj/xcshareddata/xcschemes/SFI.xcscheme @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1520" + version = "1.7"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "3AEC20F22A459AB400A63465" + BuildableName = "sing-box.app" + BlueprintName = "SFI" + ReferencedContainer = "container:sing-box.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + shouldAutocreateTestPlan = "YES"> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "3AEC20F22A459AB400A63465" + BuildableName = "sing-box.app" + BlueprintName = "SFI" + ReferencedContainer = "container:sing-box.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "3AEC20F22A459AB400A63465" + BuildableName = "sing-box.app" + BlueprintName = "SFI" + ReferencedContainer = "container:sing-box.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/sing-box.xcodeproj/xcshareddata/xcschemes/SFM.System.xcscheme b/sing-box.xcodeproj/xcshareddata/xcschemes/SFM.System.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..bfdedf970cca8936e551a01e85a807f4c9480f1a --- /dev/null +++ b/sing-box.xcodeproj/xcshareddata/xcschemes/SFM.System.xcscheme @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1520" + version = "1.7"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "3AEECC032A6DF9CA006A0E0C" + BuildableName = "SFM.app" + BlueprintName = "SFM.System" + ReferencedContainer = "container:sing-box.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + shouldAutocreateTestPlan = "YES"> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "3AEECC032A6DF9CA006A0E0C" + BuildableName = "SFM.app" + BlueprintName = "SFM.System" + ReferencedContainer = "container:sing-box.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "3AEECC032A6DF9CA006A0E0C" + BuildableName = "SFM.app" + BlueprintName = "SFM.System" + ReferencedContainer = "container:sing-box.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/sing-box.xcodeproj/xcshareddata/xcschemes/SFM.xcscheme b/sing-box.xcodeproj/xcshareddata/xcschemes/SFM.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..682e1e9c4e43bfb44678ad6f25686a68bd45a1d5 --- /dev/null +++ b/sing-box.xcodeproj/xcshareddata/xcschemes/SFM.xcscheme @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1520" + version = "1.7"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "3AEC21082A459B1900A63465" + BuildableName = "sing-box.app" + BlueprintName = "SFM" + ReferencedContainer = "container:sing-box.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + shouldAutocreateTestPlan = "YES"> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "3AEC21082A459B1900A63465" + BuildableName = "sing-box.app" + BlueprintName = "SFM" + ReferencedContainer = "container:sing-box.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "3AEC21082A459B1900A63465" + BuildableName = "sing-box.app" + BlueprintName = "SFM" + ReferencedContainer = "container:sing-box.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/sing-box.xcodeproj/xcshareddata/xcschemes/SFT.xcscheme b/sing-box.xcodeproj/xcshareddata/xcschemes/SFT.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..85b788a09faeae9e32a007ee467da02459ea738c --- /dev/null +++ b/sing-box.xcodeproj/xcshareddata/xcschemes/SFT.xcscheme @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1520" + version = "1.7"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "3AC03B952A72BF3300B7946F" + BuildableName = "sing-box.app" + BlueprintName = "SFT" + ReferencedContainer = "container:sing-box.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + shouldAutocreateTestPlan = "YES"> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "3AC03B952A72BF3300B7946F" + BuildableName = "sing-box.app" + BlueprintName = "SFT" + ReferencedContainer = "container:sing-box.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "3AC03B952A72BF3300B7946F" + BuildableName = "sing-box.app" + BlueprintName = "SFT" + ReferencedContainer = "container:sing-box.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/sing-box.xcodeproj/xcuserdata/g.makhoul.xcuserdatad/xcschemes/xcschememanagement.plist b/sing-box.xcodeproj/xcuserdata/g.makhoul.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000000000000000000000000000000000000..3fd16c7b958fdd0ee1bdab21ad7e72aa76012b87 --- /dev/null +++ b/sing-box.xcodeproj/xcuserdata/g.makhoul.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,133 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>SchemeUserState</key> + <dict> + <key>ApplicationLibrary.xcscheme_^#shared#^_</key> + <dict> + <key>orderHint</key> + <integer>5</integer> + </dict> + <key>Associations (Playground) 1.xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>15</integer> + </dict> + <key>Associations (Playground) 2.xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>16</integer> + </dict> + <key>Associations (Playground).xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>14</integer> + </dict> + <key>Extension.xcscheme_^#shared#^_</key> + <dict> + <key>orderHint</key> + <integer>4</integer> + </dict> + <key>IntentsExtension.xcscheme_^#shared#^_</key> + <dict> + <key>orderHint</key> + <integer>6</integer> + </dict> + <key>Library.xcscheme_^#shared#^_</key> + <dict> + <key>orderHint</key> + <integer>7</integer> + </dict> + <key>MyPlayground (Playground) 1.xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>18</integer> + </dict> + <key>MyPlayground (Playground) 2.xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>19</integer> + </dict> + <key>MyPlayground (Playground).xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>17</integer> + </dict> + <key>SFI.xcscheme_^#shared#^_</key> + <dict> + <key>orderHint</key> + <integer>0</integer> + </dict> + <key>SFM.System.xcscheme_^#shared#^_</key> + <dict> + <key>orderHint</key> + <integer>2</integer> + </dict> + <key>SFM.xcscheme_^#shared#^_</key> + <dict> + <key>orderHint</key> + <integer>1</integer> + </dict> + <key>SFT.xcscheme_^#shared#^_</key> + <dict> + <key>orderHint</key> + <integer>3</integer> + </dict> + <key>Tour (Playground) 1.xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>24</integer> + </dict> + <key>Tour (Playground) 2.xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>25</integer> + </dict> + <key>Tour (Playground).xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>23</integer> + </dict> + <key>TransactionObserver (Playground) 1.xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>21</integer> + </dict> + <key>TransactionObserver (Playground) 2.xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>22</integer> + </dict> + <key>TransactionObserver (Playground).xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>20</integer> + </dict> + </dict> +</dict> +</plist> diff --git a/sing-box.xcodeproj/xcuserdata/mohammedarab.xcuserdatad/xcschemes/xcschememanagement.plist b/sing-box.xcodeproj/xcuserdata/mohammedarab.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000000000000000000000000000000000000..319e3d4d92bf01ddaad2df630ff0784ab4f66458 --- /dev/null +++ b/sing-box.xcodeproj/xcuserdata/mohammedarab.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,148 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>SchemeUserState</key> + <dict> + <key>ApplicationLibrary.xcscheme_^#shared#^_</key> + <dict> + <key>orderHint</key> + <integer>6</integer> + </dict> + <key>Associations (Playground) 1.xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>18</integer> + </dict> + <key>Associations (Playground) 2.xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>19</integer> + </dict> + <key>Associations (Playground).xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>17</integer> + </dict> + <key>Extension.xcscheme_^#shared#^_</key> + <dict> + <key>orderHint</key> + <integer>7</integer> + </dict> + <key>IntentsExtension.xcscheme_^#shared#^_</key> + <dict> + <key>orderHint</key> + <integer>5</integer> + </dict> + <key>Library.xcscheme_^#shared#^_</key> + <dict> + <key>orderHint</key> + <integer>4</integer> + </dict> + <key>MacLibrary.xcscheme_^#shared#^_</key> + <dict> + <key>orderHint</key> + <integer>9</integer> + </dict> + <key>MyPlayground (Playground) 1.xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>21</integer> + </dict> + <key>MyPlayground (Playground) 2.xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>22</integer> + </dict> + <key>MyPlayground (Playground).xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>20</integer> + </dict> + <key>SFI.xcscheme_^#shared#^_</key> + <dict> + <key>orderHint</key> + <integer>0</integer> + </dict> + <key>SFM.System.xcscheme_^#shared#^_</key> + <dict> + <key>orderHint</key> + <integer>2</integer> + </dict> + <key>SFM.xcscheme_^#shared#^_</key> + <dict> + <key>orderHint</key> + <integer>1</integer> + </dict> + <key>SFT.xcscheme_^#shared#^_</key> + <dict> + <key>orderHint</key> + <integer>3</integer> + </dict> + <key>SystemExtension.xcscheme_^#shared#^_</key> + <dict> + <key>orderHint</key> + <integer>5</integer> + </dict> + <key>TVExtension.xcscheme_^#shared#^_</key> + <dict> + <key>orderHint</key> + <integer>10</integer> + </dict> + <key>Tour (Playground) 1.xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>24</integer> + </dict> + <key>Tour (Playground) 2.xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>25</integer> + </dict> + <key>Tour (Playground).xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>23</integer> + </dict> + <key>TransactionObserver (Playground) 1.xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>27</integer> + </dict> + <key>TransactionObserver (Playground) 2.xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>28</integer> + </dict> + <key>TransactionObserver (Playground).xcscheme</key> + <dict> + <key>isShown</key> + <false/> + <key>orderHint</key> + <integer>26</integer> + </dict> + </dict> +</dict> +</plist>