Restart service when settings change
This commit is contained in:
@@ -139,7 +139,7 @@ struct ProfilePickerSheet: View {
|
|||||||
.environment(\.editMode, $editMode)
|
.environment(\.editMode, $editMode)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .confirmationAction) {
|
ToolbarItem(placement: .confirmationAction) {
|
||||||
TVToolbarButton(title: editMode.isEditing ? String(localized: "Done"): String(localized: "Edit")) {
|
TVToolbarButton(title: editMode.isEditing ? String(localized: "Done") : String(localized: "Edit")) {
|
||||||
withAnimation {
|
withAnimation {
|
||||||
if editMode.isEditing {
|
if editMode.isEditing {
|
||||||
movingProfileID = nil
|
movingProfileID = nil
|
||||||
|
|||||||
@@ -32,17 +32,7 @@ public final class OverviewViewModel: BaseViewModel {
|
|||||||
try LibboxNewStandaloneCommandClient()!.setSystemProxyEnabled(enabled)
|
try LibboxNewStandaloneCommandClient()!.setSystemProxyEnabled(enabled)
|
||||||
} else {
|
} else {
|
||||||
await MainActor.run { reasserting = true }
|
await MainActor.run { reasserting = true }
|
||||||
try await profile.stop()
|
try await profile.restart()
|
||||||
|
|
||||||
var waitSeconds = 0
|
|
||||||
while await profile.status != .disconnected {
|
|
||||||
try await Task.sleep(nanoseconds: NSEC_PER_SEC)
|
|
||||||
waitSeconds += 1
|
|
||||||
if waitSeconds >= 5 {
|
|
||||||
throw NSError(domain: "Restart service timeout", code: 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try await profile.start()
|
|
||||||
await MainActor.run { reasserting = false }
|
await MainActor.run { reasserting = false }
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ import Library
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
public struct OnDemandRulesView: View {
|
public struct OnDemandRulesView: View {
|
||||||
|
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||||
@State private var isLoading = true
|
@State private var isLoading = true
|
||||||
|
@State private var alert: AlertState?
|
||||||
@State private var alwaysOn = false
|
@State private var alwaysOn = false
|
||||||
|
|
||||||
public init() {}
|
public init() {}
|
||||||
@@ -23,11 +25,13 @@ public struct OnDemandRulesView: View {
|
|||||||
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.
|
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.
|
||||||
""", $alwaysOn) { newValue in
|
""", $alwaysOn) { newValue in
|
||||||
await SharedPreferences.alwaysOn.set(newValue)
|
await SharedPreferences.alwaysOn.set(newValue)
|
||||||
|
await restartService()
|
||||||
}
|
}
|
||||||
|
|
||||||
FormButton {
|
FormButton {
|
||||||
Task {
|
Task {
|
||||||
await SharedPreferences.resetOnDemandRules()
|
await SharedPreferences.resetOnDemandRules()
|
||||||
|
await restartService()
|
||||||
isLoading = true
|
isLoading = true
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
@@ -38,11 +42,23 @@ public struct OnDemandRulesView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("On Demand Rules")
|
.navigationTitle("On Demand Rules")
|
||||||
|
.alert($alert)
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func restartService() async {
|
||||||
|
guard let profile = environments.extensionProfile, profile.status.isConnected else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
try await profile.restart()
|
||||||
|
} catch {
|
||||||
|
alert = AlertState(error: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func loadSettings() async {
|
private func loadSettings() async {
|
||||||
alwaysOn = await SharedPreferences.alwaysOn.get()
|
alwaysOn = await SharedPreferences.alwaysOn.get()
|
||||||
isLoading = false
|
isLoading = false
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ import Library
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct PacketTunnelView: View {
|
struct PacketTunnelView: View {
|
||||||
|
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||||
@State private var isLoading = true
|
@State private var isLoading = true
|
||||||
|
@State private var alert: AlertState?
|
||||||
|
|
||||||
@State private var ignoreMemoryLimit = false
|
@State private var ignoreMemoryLimit = false
|
||||||
|
|
||||||
@@ -27,6 +29,7 @@ struct PacketTunnelView: View {
|
|||||||
Do not enforce memory limits on sing-box. Will cause OOM on non-jailbroken iOS and tvOS devices.
|
Do not enforce memory limits on sing-box. Will cause OOM on non-jailbroken iOS and tvOS devices.
|
||||||
""", $ignoreMemoryLimit) { newValue in
|
""", $ignoreMemoryLimit) { newValue in
|
||||||
await SharedPreferences.ignoreMemoryLimit.set(newValue)
|
await SharedPreferences.ignoreMemoryLimit.set(newValue)
|
||||||
|
await restartService()
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
@@ -38,6 +41,7 @@ struct PacketTunnelView: View {
|
|||||||
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/3131931-includeallnetworks)
|
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/3131931-includeallnetworks)
|
||||||
""", $includeAllNetworks) { newValue in
|
""", $includeAllNetworks) { newValue in
|
||||||
await SharedPreferences.includeAllNetworks.set(newValue)
|
await SharedPreferences.includeAllNetworks.set(newValue)
|
||||||
|
await restartService()
|
||||||
}
|
}
|
||||||
|
|
||||||
FormToggle("excludeAPNs", """
|
FormToggle("excludeAPNs", """
|
||||||
@@ -46,14 +50,16 @@ struct PacketTunnelView: View {
|
|||||||
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/4140516-excludeapns)
|
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/4140516-excludeapns)
|
||||||
""", $excludeAPNs) { newValue in
|
""", $excludeAPNs) { newValue in
|
||||||
await SharedPreferences.excludeAPNs.set(newValue)
|
await SharedPreferences.excludeAPNs.set(newValue)
|
||||||
|
await restartService()
|
||||||
}
|
}
|
||||||
|
|
||||||
FormToggle("excludeCellularServices", """
|
FormToggle("excludeCellularServices", """
|
||||||
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.
|
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)
|
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/4140517-excludecellularservices)
|
||||||
""", $excludeCellularServices) { newValue in
|
""", $excludeCellularServices) { newValue in
|
||||||
await SharedPreferences.excludeCellularServices.set(newValue)
|
await SharedPreferences.excludeCellularServices.set(newValue)
|
||||||
|
await restartService()
|
||||||
}
|
}
|
||||||
|
|
||||||
FormToggle("excludeLocalNetworks", """
|
FormToggle("excludeLocalNetworks", """
|
||||||
@@ -62,6 +68,7 @@ struct PacketTunnelView: View {
|
|||||||
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/3143658-excludelocalnetworks)
|
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/3143658-excludelocalnetworks)
|
||||||
""", $excludeLocalNetworks) { newValue in
|
""", $excludeLocalNetworks) { newValue in
|
||||||
await SharedPreferences.excludeLocalNetworks.set(newValue)
|
await SharedPreferences.excludeLocalNetworks.set(newValue)
|
||||||
|
await restartService()
|
||||||
}
|
}
|
||||||
|
|
||||||
FormToggle("enforceRoutes", """
|
FormToggle("enforceRoutes", """
|
||||||
@@ -72,6 +79,7 @@ struct PacketTunnelView: View {
|
|||||||
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/3689459-enforceroutes)
|
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/3689459-enforceroutes)
|
||||||
""", $enforceRoutes) { newValue in
|
""", $enforceRoutes) { newValue in
|
||||||
await SharedPreferences.enforceRoutes.set(newValue)
|
await SharedPreferences.enforceRoutes.set(newValue)
|
||||||
|
await restartService()
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -79,6 +87,7 @@ struct PacketTunnelView: View {
|
|||||||
FormButton {
|
FormButton {
|
||||||
Task {
|
Task {
|
||||||
await SharedPreferences.resetPacketTunnel()
|
await SharedPreferences.resetPacketTunnel()
|
||||||
|
await restartService()
|
||||||
isLoading = true
|
isLoading = true
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
@@ -89,11 +98,23 @@ struct PacketTunnelView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("Packet Tunnel")
|
.navigationTitle("Packet Tunnel")
|
||||||
|
.alert($alert)
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func restartService() async {
|
||||||
|
guard let profile = environments.extensionProfile, profile.status.isConnected else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
try await profile.restart()
|
||||||
|
} catch {
|
||||||
|
alert = AlertState(error: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func loadSettings() async {
|
private func loadSettings() async {
|
||||||
ignoreMemoryLimit = await SharedPreferences.ignoreMemoryLimit.get()
|
ignoreMemoryLimit = await SharedPreferences.ignoreMemoryLimit.get()
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
|
import Libbox
|
||||||
import Library
|
import Library
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
public struct ProfileOverrideView: View {
|
public struct ProfileOverrideView: View {
|
||||||
|
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||||
@State private var isLoading = true
|
@State private var isLoading = true
|
||||||
|
@State private var alert: AlertState?
|
||||||
@State private var excludeDefaultRoute = false
|
@State private var excludeDefaultRoute = false
|
||||||
@State private var autoRouteUseSubRangesByDefault = false
|
@State private var autoRouteUseSubRangesByDefault = false
|
||||||
@State private var excludeAPNsRoute = false
|
@State private var excludeAPNsRoute = false
|
||||||
@@ -20,6 +23,7 @@ public struct ProfileOverrideView: View {
|
|||||||
FormView {
|
FormView {
|
||||||
FormToggle("Hide VPN Icon", "Append `0.0.0.0/31` and `::/127` to `route_exclude_address` if not exists.", $excludeDefaultRoute) { newValue in
|
FormToggle("Hide VPN Icon", "Append `0.0.0.0/31` and `::/127` to `route_exclude_address` if not exists.", $excludeDefaultRoute) { newValue in
|
||||||
await SharedPreferences.excludeDefaultRoute.set(newValue)
|
await SharedPreferences.excludeDefaultRoute.set(newValue)
|
||||||
|
await reloadService()
|
||||||
}
|
}
|
||||||
|
|
||||||
FormToggle("No Default Route", """
|
FormToggle("No Default Route", """
|
||||||
@@ -27,15 +31,18 @@ public struct ProfileOverrideView: View {
|
|||||||
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).
|
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).
|
||||||
""", $autoRouteUseSubRangesByDefault) { newValue in
|
""", $autoRouteUseSubRangesByDefault) { newValue in
|
||||||
await SharedPreferences.autoRouteUseSubRangesByDefault.set(newValue)
|
await SharedPreferences.autoRouteUseSubRangesByDefault.set(newValue)
|
||||||
|
await reloadService()
|
||||||
}
|
}
|
||||||
|
|
||||||
FormToggle("Exclude APNs Route", "Append `push.apple.com` to `bypass_domain`, and `17.0.0.0/8` to `route_exclude_address`.", $excludeAPNsRoute) { newValue in
|
FormToggle("Exclude APNs Route", "Append `push.apple.com` to `bypass_domain`, and `17.0.0.0/8` to `route_exclude_address`.", $excludeAPNsRoute) { newValue in
|
||||||
await SharedPreferences.excludeAPNsRoute.set(newValue)
|
await SharedPreferences.excludeAPNsRoute.set(newValue)
|
||||||
|
await reloadService()
|
||||||
}
|
}
|
||||||
|
|
||||||
FormButton {
|
FormButton {
|
||||||
Task {
|
Task {
|
||||||
await SharedPreferences.resetProfileOverride()
|
await SharedPreferences.resetProfileOverride()
|
||||||
|
await reloadService()
|
||||||
isLoading = true
|
isLoading = true
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
@@ -46,11 +53,23 @@ public struct ProfileOverrideView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("Profile Override")
|
.navigationTitle("Profile Override")
|
||||||
|
.alert($alert)
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func reloadService() async {
|
||||||
|
guard let profile = environments.extensionProfile, profile.status.isConnected else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
try LibboxNewStandaloneCommandClient()?.serviceReload()
|
||||||
|
} catch {
|
||||||
|
alert = AlertState(error: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func loadSettings() async {
|
private func loadSettings() async {
|
||||||
excludeDefaultRoute = await SharedPreferences.excludeDefaultRoute.get()
|
excludeDefaultRoute = await SharedPreferences.excludeDefaultRoute.get()
|
||||||
autoRouteUseSubRangesByDefault = await SharedPreferences.autoRouteUseSubRangesByDefault.get()
|
autoRouteUseSubRangesByDefault = await SharedPreferences.autoRouteUseSubRangesByDefault.get()
|
||||||
|
|||||||
@@ -38,9 +38,7 @@ struct StartServiceIntent: AppIntent {
|
|||||||
}
|
}
|
||||||
try LibboxNewStandaloneCommandClient()!.serviceReload()
|
try LibboxNewStandaloneCommandClient()!.serviceReload()
|
||||||
} else if extensionProfile.status.isConnected {
|
} else if extensionProfile.status.isConnected {
|
||||||
try await extensionProfile.stop()
|
try await extensionProfile.restart()
|
||||||
try await Task.sleep(nanoseconds: UInt64(100 * Double(NSEC_PER_MSEC)))
|
|
||||||
try await extensionProfile.start()
|
|
||||||
} else {
|
} else {
|
||||||
try await extensionProfile.start()
|
try await extensionProfile.start()
|
||||||
}
|
}
|
||||||
@@ -65,9 +63,7 @@ struct RestartServiceIntent: AppIntent {
|
|||||||
if extensionProfile.status == .connected {
|
if extensionProfile.status == .connected {
|
||||||
try LibboxNewStandaloneCommandClient()!.serviceReload()
|
try LibboxNewStandaloneCommandClient()!.serviceReload()
|
||||||
} else if extensionProfile.status.isConnected {
|
} else if extensionProfile.status.isConnected {
|
||||||
try await extensionProfile.stop()
|
try await extensionProfile.restart()
|
||||||
try await Task.sleep(nanoseconds: UInt64(100 * Double(NSEC_PER_MSEC)))
|
|
||||||
try await extensionProfile.start()
|
|
||||||
} else {
|
} else {
|
||||||
try await extensionProfile.start()
|
try await extensionProfile.start()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,6 +108,19 @@ public class ExtensionProfile: ObservableObject {
|
|||||||
manager.connection.stopVPNTunnel()
|
manager.connection.stopVPNTunnel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func restart() async throws {
|
||||||
|
try await stop()
|
||||||
|
var waitSeconds = 0
|
||||||
|
while await MainActor.run(body: { status }) != .disconnected {
|
||||||
|
try await Task.sleep(nanoseconds: NSEC_PER_SEC)
|
||||||
|
waitSeconds += 1
|
||||||
|
if waitSeconds >= 5 {
|
||||||
|
throw NSError(domain: "Restart service timeout", code: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try await start()
|
||||||
|
}
|
||||||
|
|
||||||
public static func load() async throws -> ExtensionProfile? {
|
public static func load() async throws -> ExtensionProfile? {
|
||||||
let managers = try await NETunnelProviderManager.loadAllFromPreferences()
|
let managers = try await NETunnelProviderManager.loadAllFromPreferences()
|
||||||
if managers.isEmpty {
|
if managers.isEmpty {
|
||||||
|
|||||||
@@ -923,7 +923,7 @@
|
|||||||
"If this property is true, the system excludes Apple Push Notification services (APNs) traffic, but only when the **includeAllNetworks** property is also true.\n\n[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/4140516-excludeapns)" : {
|
"If this property is true, the system excludes Apple Push Notification services (APNs) traffic, but only when the **includeAllNetworks** property is also true.\n\n[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/4140516-excludeapns)" : {
|
||||||
"shouldTranslate" : false
|
"shouldTranslate" : false
|
||||||
},
|
},
|
||||||
"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.\n\n[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/4140517-excludecellularservices)" : {
|
"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.\n\n[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/4140517-excludecellularservices)" : {
|
||||||
"shouldTranslate" : false
|
"shouldTranslate" : false
|
||||||
},
|
},
|
||||||
"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.\n\n[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/3143658-excludelocalnetworks)" : {
|
"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.\n\n[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/3143658-excludelocalnetworks)" : {
|
||||||
|
|||||||
Reference in New Issue
Block a user