Add clash mode selector & always on

This commit is contained in:
世界
2023-08-28 11:42:47 +08:00
parent d3b2040fe1
commit b5a89fddec
11 changed files with 169 additions and 13 deletions
@@ -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 {
-4
View File
@@ -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>
+1
View File
@@ -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
+21
View File
@@ -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
}
}
} }
+19
View File
@@ -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()
} }
+14 -8
View File
@@ -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 = "";