Add clash mode selector & always on
This commit is contained in:
@@ -0,0 +1,58 @@
|
|||||||
|
import Libbox
|
||||||
|
import Library
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
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.detached {
|
||||||
|
await setMode(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 func setMode(_ newMode: String) {
|
||||||
|
do {
|
||||||
|
try LibboxNewStandaloneCommandClient()!.setClashMode(newMode)
|
||||||
|
} catch {
|
||||||
|
alert = Alert(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import Library
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
public struct ExtensionStatusView: View {
|
public struct ExtensionStatusView: View {
|
||||||
|
@Environment(\.scenePhase) private var scenePhase
|
||||||
@StateObject private var commandClient = CommandClient(.status)
|
@StateObject private var commandClient = CommandClient(.status)
|
||||||
@State private var columnCount: Int = 4
|
@State private var columnCount: Int = 4
|
||||||
@State private var alert: Alert?
|
@State private var alert: Alert?
|
||||||
@@ -83,6 +84,13 @@ public struct ExtensionStatusView: View {
|
|||||||
.onDisappear {
|
.onDisappear {
|
||||||
commandClient.disconnect()
|
commandClient.disconnect()
|
||||||
}
|
}
|
||||||
|
.onChangeCompat(of: scenePhase) { newValue in
|
||||||
|
if newValue == .active {
|
||||||
|
commandClient.connect()
|
||||||
|
} else {
|
||||||
|
commandClient.disconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
.alertBinding($alert)
|
.alertBinding($alert)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import SwiftUI
|
|||||||
public struct OverviewView: View {
|
public struct OverviewView: View {
|
||||||
public static let NotificationUpdateSelectedProfile = Notification.Name("update-selected-profile")
|
public static let NotificationUpdateSelectedProfile = Notification.Name("update-selected-profile")
|
||||||
|
|
||||||
@Environment(\.scenePhase) var scenePhase
|
|
||||||
@Environment(\.selection) private var selection
|
@Environment(\.selection) private var selection
|
||||||
@EnvironmentObject private var profile: ExtensionProfile
|
@EnvironmentObject private var profile: ExtensionProfile
|
||||||
@Binding private var profileList: [Profile]
|
@Binding private var profileList: [Profile]
|
||||||
@@ -24,6 +23,7 @@ public struct OverviewView: View {
|
|||||||
VStack {
|
VStack {
|
||||||
if ApplicationLibrary.inPreview || profile.status.isConnected {
|
if ApplicationLibrary.inPreview || profile.status.isConnected {
|
||||||
ExtensionStatusView()
|
ExtensionStatusView()
|
||||||
|
ClashModeView()
|
||||||
}
|
}
|
||||||
if profileList.isEmpty {
|
if profileList.isEmpty {
|
||||||
Text("Empty profiles")
|
Text("Empty profiles")
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import Library
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
public struct GroupListView: View {
|
public struct GroupListView: View {
|
||||||
|
@Environment(\.scenePhase) private var scenePhase
|
||||||
@State private var isLoading = true
|
@State private var isLoading = true
|
||||||
@StateObject private var commandClient = CommandClient(.groups)
|
@StateObject private var commandClient = CommandClient(.groups)
|
||||||
@State private var groups: [OutboundGroup] = []
|
@State private var groups: [OutboundGroup] = []
|
||||||
@@ -31,6 +32,13 @@ public struct GroupListView: View {
|
|||||||
.onDisappear {
|
.onDisappear {
|
||||||
commandClient.disconnect()
|
commandClient.disconnect()
|
||||||
}
|
}
|
||||||
|
.onChangeCompat(of: scenePhase) { newValue in
|
||||||
|
if newValue == .active {
|
||||||
|
commandClient.connect()
|
||||||
|
} else {
|
||||||
|
commandClient.disconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
.onReceive(commandClient.$groups, perform: { groups in
|
.onReceive(commandClient.$groups, perform: { groups in
|
||||||
if let groups {
|
if let groups {
|
||||||
setGroups(groups)
|
setGroups(groups)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ public struct SettingView: View {
|
|||||||
@State private var keepMenuBarInBackground = false
|
@State private var keepMenuBarInBackground = false
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@State private var alwaysOn = false
|
||||||
@State private var disableMemoryLimit = false
|
@State private var disableMemoryLimit = false
|
||||||
@State private var version = ""
|
@State private var version = ""
|
||||||
@State private var dataSize = ""
|
@State private var dataSize = ""
|
||||||
@@ -66,6 +67,13 @@ public struct SettingView: View {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
Section("Packet Tunnel") {
|
Section("Packet Tunnel") {
|
||||||
|
Toggle("Always On", isOn: $alwaysOn)
|
||||||
|
.onChangeCompat(of: alwaysOn) { newValue in
|
||||||
|
Task.detached {
|
||||||
|
SharedPreferences.alwaysOn = newValue
|
||||||
|
await updateAlwaysOn(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
Toggle("Disable Memory Limit", isOn: $disableMemoryLimit)
|
Toggle("Disable Memory Limit", isOn: $disableMemoryLimit)
|
||||||
.onChangeCompat(of: disableMemoryLimit) { newValue in
|
.onChangeCompat(of: disableMemoryLimit) { newValue in
|
||||||
Task.detached {
|
Task.detached {
|
||||||
@@ -155,6 +163,7 @@ public struct SettingView: View {
|
|||||||
startAtLogin = SMAppService.mainApp.status == .enabled
|
startAtLogin = SMAppService.mainApp.status == .enabled
|
||||||
keepMenuBarInBackground = SharedPreferences.menuBarExtraInBackground
|
keepMenuBarInBackground = SharedPreferences.menuBarExtraInBackground
|
||||||
#endif
|
#endif
|
||||||
|
alwaysOn = SharedPreferences.alwaysOn
|
||||||
disableMemoryLimit = SharedPreferences.disableMemoryLimit
|
disableMemoryLimit = SharedPreferences.disableMemoryLimit
|
||||||
version = LibboxVersion()
|
version = LibboxVersion()
|
||||||
if ApplicationLibrary.inPreview {
|
if ApplicationLibrary.inPreview {
|
||||||
@@ -173,6 +182,17 @@ public struct SettingView: View {
|
|||||||
try? FileManager.default.removeItem(at: FilePath.workingDirectory)
|
try? FileManager.default.removeItem(at: FilePath.workingDirectory)
|
||||||
isLoading = true
|
isLoading = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func updateAlwaysOn(_ newState: Bool) async {
|
||||||
|
guard let profile = try? await ExtensionProfile.load() else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
try await profile.updateAlwaysOn(newState)
|
||||||
|
} catch {
|
||||||
|
alert = Alert(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension URL {
|
private extension URL {
|
||||||
|
|||||||
@@ -2,10 +2,6 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>UIRequiredDeviceCapabilities</key>
|
|
||||||
<array>
|
|
||||||
<string>arm64</string>
|
|
||||||
</array>
|
|
||||||
<key>NSExtension</key>
|
<key>NSExtension</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSExtensionPointIdentifier</key>
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ public enum SharedPreferences {
|
|||||||
@Preference<Bool>("disable_memory_limit", defaultValue: disableMemoryLimitByDefault) public static var disableMemoryLimit
|
@Preference<Bool>("disable_memory_limit", defaultValue: disableMemoryLimitByDefault) public static var disableMemoryLimit
|
||||||
|
|
||||||
@Preference<Int>("max_log_lines", defaultValue: 300) public static var maxLogLines
|
@Preference<Int>("max_log_lines", defaultValue: 300) public static var maxLogLines
|
||||||
|
@Preference<Bool>("always_on", defaultValue: false) public static var alwaysOn
|
||||||
|
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
@Preference<Bool>("show_menu_bar_extra", defaultValue: true) public static var showMenuBarExtra
|
@Preference<Bool>("show_menu_bar_extra", defaultValue: true) public static var showMenuBarExtra
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ public class CommandClient: ObservableObject {
|
|||||||
case status
|
case status
|
||||||
case groups
|
case groups
|
||||||
case log
|
case log
|
||||||
|
case clashMode
|
||||||
}
|
}
|
||||||
|
|
||||||
private let connectionType: ConnectionType
|
private let connectionType: ConnectionType
|
||||||
@@ -17,11 +18,15 @@ public class CommandClient: ObservableObject {
|
|||||||
@Published public var status: LibboxStatusMessage?
|
@Published public var status: LibboxStatusMessage?
|
||||||
@Published public var groups: [LibboxOutboundGroup]?
|
@Published public var groups: [LibboxOutboundGroup]?
|
||||||
@Published public var logList: [String]
|
@Published public var logList: [String]
|
||||||
|
@Published public var clashModeList: [String]
|
||||||
|
@Published public var clashMode: String
|
||||||
|
|
||||||
public init(_ connectionType: ConnectionType, logMaxLines: Int = 300) {
|
public init(_ connectionType: ConnectionType, logMaxLines: Int = 300) {
|
||||||
self.connectionType = connectionType
|
self.connectionType = connectionType
|
||||||
self.logMaxLines = logMaxLines
|
self.logMaxLines = logMaxLines
|
||||||
logList = []
|
logList = []
|
||||||
|
clashModeList = []
|
||||||
|
clashMode = ""
|
||||||
isConnected = false
|
isConnected = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +62,8 @@ public class CommandClient: ObservableObject {
|
|||||||
clientOptions.command = LibboxCommandGroup
|
clientOptions.command = LibboxCommandGroup
|
||||||
case .log:
|
case .log:
|
||||||
clientOptions.command = LibboxCommandLog
|
clientOptions.command = LibboxCommandLog
|
||||||
|
case .clashMode:
|
||||||
|
clientOptions.command = LibboxCommandClashMode
|
||||||
}
|
}
|
||||||
clientOptions.statusInterval = Int64(2 * NSEC_PER_SEC)
|
clientOptions.statusInterval = Int64(2 * NSEC_PER_SEC)
|
||||||
let client = LibboxNewCommandClient(clientHandler(self), clientOptions)!
|
let client = LibboxNewCommandClient(clientHandler(self), clientOptions)!
|
||||||
@@ -68,6 +75,7 @@ public class CommandClient: ObservableObject {
|
|||||||
try client.connect()
|
try client.connect()
|
||||||
commandClient = client
|
commandClient = client
|
||||||
return
|
return
|
||||||
|
} catch {
|
||||||
}
|
}
|
||||||
try Task.checkCancellation()
|
try Task.checkCancellation()
|
||||||
}
|
}
|
||||||
@@ -127,5 +135,18 @@ public class CommandClient: ObservableObject {
|
|||||||
self.commandClient.groups = newGroups
|
self.commandClient.groups = newGroups
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initializeClashMode(_ modeList: LibboxStringIteratorProtocol?, currentMode: String?) {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
self.commandClient.clashModeList = modeList!.toArray()
|
||||||
|
self.commandClient.clashMode = currentMode!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateClashMode(_ newMode: String?) {
|
||||||
|
DispatchQueue.main.sync {
|
||||||
|
self.commandClient.clashMode = newMode!
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import NetworkExtension
|
|||||||
public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtocol {
|
public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtocol {
|
||||||
private let tunnel: NEPacketTunnelProvider
|
private let tunnel: NEPacketTunnelProvider
|
||||||
private let commandServer: LibboxCommandServer
|
private let commandServer: LibboxCommandServer
|
||||||
|
private var networkSettings: NEPacketTunnelNetworkSettings?
|
||||||
|
|
||||||
init(_ tunnel: NEPacketTunnelProvider, _ logServer: LibboxCommandServer) {
|
init(_ tunnel: NEPacketTunnelProvider, _ logServer: LibboxCommandServer) {
|
||||||
self.tunnel = tunnel
|
self.tunnel = tunnel
|
||||||
@@ -88,6 +89,7 @@ public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtoc
|
|||||||
settings.proxySettings = proxySettings
|
settings.proxySettings = proxySettings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
networkSettings = settings
|
||||||
try runBlocking { [self] in
|
try runBlocking { [self] in
|
||||||
try await tunnel.setTunnelNetworkSettings(settings)
|
try await tunnel.setTunnelNetworkSettings(settings)
|
||||||
}
|
}
|
||||||
@@ -153,4 +155,21 @@ public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtoc
|
|||||||
public func underNetworkExtension() -> Bool {
|
public func underNetworkExtension() -> Bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func clearDNSCache() {
|
||||||
|
guard let networkSettings else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tunnel.reasserting = true
|
||||||
|
tunnel.setTunnelNetworkSettings(nil) { _ in
|
||||||
|
}
|
||||||
|
tunnel.setTunnelNetworkSettings(networkSettings) { _ in
|
||||||
|
}
|
||||||
|
tunnel.reasserting = false
|
||||||
|
}
|
||||||
|
|
||||||
|
public func closeTun() throws {
|
||||||
|
tunnel.setTunnelNetworkSettings(nil) { _ in
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,8 +38,26 @@ public class ExtensionProfile: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
public func start() async throws {
|
||||||
manager.isEnabled = true
|
manager.isEnabled = true
|
||||||
|
if SharedPreferences.alwaysOn {
|
||||||
|
manager.isOnDemandEnabled = true
|
||||||
|
setOnDemandRules()
|
||||||
|
}
|
||||||
try await manager.saveToPreferences()
|
try await manager.saveToPreferences()
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
if Variant.useSystemExtension {
|
if Variant.useSystemExtension {
|
||||||
@@ -53,6 +71,7 @@ public class ExtensionProfile: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func stop() {
|
public func stop() {
|
||||||
|
manager.isOnDemandEnabled = false
|
||||||
manager.connection.stopVPNTunnel()
|
manager.connection.stopVPNTunnel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@
|
|||||||
3A4EAD362A4FEB9C005435B3 /* ProfileUpdateTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A55F9592A4D1554003C4EF4 /* ProfileUpdateTask.swift */; };
|
3A4EAD362A4FEB9C005435B3 /* ProfileUpdateTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A55F9592A4D1554003C4EF4 /* ProfileUpdateTask.swift */; };
|
||||||
3A4EAD372A4FEC20005435B3 /* ApplicationLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A4EAD102A4FEAE6005435B3 /* ApplicationLibrary.framework */; };
|
3A4EAD372A4FEC20005435B3 /* ApplicationLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A4EAD102A4FEAE6005435B3 /* ApplicationLibrary.framework */; };
|
||||||
3A4EAD3C2A4FECCE005435B3 /* NEVPNStatus+isConnected.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4EAD3B2A4FECCE005435B3 /* NEVPNStatus+isConnected.swift */; };
|
3A4EAD3C2A4FECCE005435B3 /* NEVPNStatus+isConnected.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4EAD3B2A4FECCE005435B3 /* NEVPNStatus+isConnected.swift */; };
|
||||||
|
3A4F68B02A97602C003D66D3 /* ClashModeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4F68AF2A97602C003D66D3 /* ClashModeView.swift */; };
|
||||||
3A4FB1572A73467F007012B9 /* Library.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; };
|
3A4FB1572A73467F007012B9 /* Library.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; };
|
||||||
3A4FB1582A73467F007012B9 /* Library.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
3A4FB1582A73467F007012B9 /* Library.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
3A4FB15C2A73468C007012B9 /* ApplicationLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A4EAD102A4FEAE6005435B3 /* ApplicationLibrary.framework */; };
|
3A4FB15C2A73468C007012B9 /* ApplicationLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A4EAD102A4FEAE6005435B3 /* ApplicationLibrary.framework */; };
|
||||||
@@ -447,6 +448,7 @@
|
|||||||
3A4EAD102A4FEAE6005435B3 /* ApplicationLibrary.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ApplicationLibrary.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
3A4FB1672A7358C9007012B9 /* ApplicationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationDelegate.swift; sourceTree = "<group>"; };
|
||||||
@@ -963,6 +965,7 @@
|
|||||||
3AE4D0C02A6E4852009FEA9E /* InstallSystemExtensionButton.swift */,
|
3AE4D0C02A6E4852009FEA9E /* InstallSystemExtensionButton.swift */,
|
||||||
3A0C6D3B2A79D46500A4DF2B /* OverviewView.swift */,
|
3A0C6D3B2A79D46500A4DF2B /* OverviewView.swift */,
|
||||||
3A0C6D3D2A79D6A600A4DF2B /* DashboardPage.swift */,
|
3A0C6D3D2A79D6A600A4DF2B /* DashboardPage.swift */,
|
||||||
|
3A4F68AF2A97602C003D66D3 /* ClashModeView.swift */,
|
||||||
);
|
);
|
||||||
path = Dashboard;
|
path = Dashboard;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1432,6 +1435,7 @@
|
|||||||
3A4EAD242A4FEB65005435B3 /* InstallProfileButton.swift in Sources */,
|
3A4EAD242A4FEB65005435B3 /* InstallProfileButton.swift in Sources */,
|
||||||
3AE4D0C12A6E4852009FEA9E /* InstallSystemExtensionButton.swift in Sources */,
|
3AE4D0C12A6E4852009FEA9E /* InstallSystemExtensionButton.swift in Sources */,
|
||||||
3A4EAD232A4FEB5A005435B3 /* EnvironmentValues.swift in Sources */,
|
3A4EAD232A4FEB5A005435B3 /* EnvironmentValues.swift in Sources */,
|
||||||
|
3A4F68B02A97602C003D66D3 /* ClashModeView.swift in Sources */,
|
||||||
3A4EAD282A4FEB65005435B3 /* ActiveDashboardView.swift in Sources */,
|
3A4EAD282A4FEB65005435B3 /* ActiveDashboardView.swift in Sources */,
|
||||||
3A1CF2F02A50E5EE000A8289 /* GroupListView.swift in Sources */,
|
3A1CF2F02A50E5EE000A8289 /* GroupListView.swift in Sources */,
|
||||||
3A4EAD322A4FEB7B005435B3 /* ServiceLogView.swift in Sources */,
|
3A4EAD322A4FEB7B005435B3 /* ServiceLogView.swift in Sources */,
|
||||||
@@ -1699,6 +1703,7 @@
|
|||||||
INFOPLIST_FILE = Extension/Info.plist;
|
INFOPLIST_FILE = Extension/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Extension;
|
INFOPLIST_KEY_CFBundleDisplayName = Extension;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
|
INFOPLIST_KEY_UIRequiredDeviceCapabilities = arm64;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
@@ -1738,6 +1743,7 @@
|
|||||||
INFOPLIST_FILE = Extension/Info.plist;
|
INFOPLIST_FILE = Extension/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Extension;
|
INFOPLIST_KEY_CFBundleDisplayName = Extension;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
|
INFOPLIST_KEY_UIRequiredDeviceCapabilities = arm64;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
@@ -2017,13 +2023,13 @@
|
|||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator";
|
SUPPORTED_PLATFORMS = "appletvos appletvsimulator";
|
||||||
SUPPORTS_MACCATALYST = NO;
|
SUPPORTS_MACCATALYST = NO;
|
||||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,3";
|
TARGETED_DEVICE_FAMILY = 3;
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
@@ -2049,13 +2055,13 @@
|
|||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator";
|
SUPPORTED_PLATFORMS = "appletvos appletvsimulator";
|
||||||
SUPPORTS_MACCATALYST = NO;
|
SUPPORTS_MACCATALYST = NO;
|
||||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,3";
|
TARGETED_DEVICE_FAMILY = 3;
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
@@ -2475,7 +2481,7 @@
|
|||||||
"@executable_path/../../../../Frameworks",
|
"@executable_path/../../../../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = "1.4.0-beta.5";
|
MARKETING_VERSION = 1.4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.system;
|
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.system;
|
||||||
PRODUCT_NAME = "$(inherited)";
|
PRODUCT_NAME = "$(inherited)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@@ -2511,7 +2517,7 @@
|
|||||||
"@executable_path/../../../../Frameworks",
|
"@executable_path/../../../../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = "1.4.0-beta.5";
|
MARKETING_VERSION = 1.4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.system;
|
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.system;
|
||||||
PRODUCT_NAME = "$(inherited)";
|
PRODUCT_NAME = "$(inherited)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@@ -2551,7 +2557,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = "1.4.0-beta.6";
|
MARKETING_VERSION = 1.4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.independent;
|
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.independent;
|
||||||
PRODUCT_NAME = SFM;
|
PRODUCT_NAME = SFM;
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@@ -2590,7 +2596,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = "1.4.0-beta.6";
|
MARKETING_VERSION = 1.4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.independent;
|
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.independent;
|
||||||
PRODUCT_NAME = SFM;
|
PRODUCT_NAME = SFM;
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
|||||||
Reference in New Issue
Block a user