Files
ProxyIOS/ProxyCore/Sources/Shared/IPCManager.swift
2026-04-06 11:28:40 -05:00

104 lines
3.5 KiB
Swift

import Foundation
/// Lightweight IPC between the main app and the packet tunnel extension
/// using Darwin notifications (fire-and-forget signals) and shared UserDefaults.
public final class IPCManager: Sendable {
public static let shared = IPCManager()
private let suiteName = "group.com.treyt.proxyapp"
public enum Notification: String, Sendable {
case newTrafficCaptured = "com.treyt.proxyapp.newTraffic"
case configurationChanged = "com.treyt.proxyapp.configChanged"
case extensionStarted = "com.treyt.proxyapp.extensionStarted"
case extensionStopped = "com.treyt.proxyapp.extensionStopped"
}
private init() {}
// MARK: - Darwin Notifications
public func post(_ notification: Notification) {
let name = CFNotificationName(notification.rawValue as CFString)
CFNotificationCenterPostNotification(
CFNotificationCenterGetDarwinNotifyCenter(),
name, nil, nil, true
)
}
public func observe(_ notification: Notification, callback: @escaping @Sendable () -> Void) {
let name = notification.rawValue as CFString
let center = CFNotificationCenterGetDarwinNotifyCenter()
// Store callback in a static dictionary keyed by notification name
DarwinCallbackStore.shared.register(name: notification.rawValue, callback: callback)
CFNotificationCenterAddObserver(
center, nil,
{ _, _, name, _, _ in
guard let cfName = name?.rawValue as? String else { return }
DarwinCallbackStore.shared.fire(name: cfName)
},
name, nil,
.deliverImmediately
)
}
// MARK: - Shared UserDefaults
public var sharedDefaults: UserDefaults? {
UserDefaults(suiteName: suiteName)
}
public var isSSLProxyingEnabled: Bool {
get { sharedDefaults?.bool(forKey: "sslProxyingEnabled") ?? false }
set { sharedDefaults?.set(newValue, forKey: "sslProxyingEnabled") }
}
public var isBlockListEnabled: Bool {
get { sharedDefaults?.bool(forKey: "blockListEnabled") ?? false }
set { sharedDefaults?.set(newValue, forKey: "blockListEnabled") }
}
public var isBreakpointEnabled: Bool {
get { sharedDefaults?.bool(forKey: "breakpointEnabled") ?? false }
set { sharedDefaults?.set(newValue, forKey: "breakpointEnabled") }
}
public var isNoCachingEnabled: Bool {
get { sharedDefaults?.bool(forKey: "noCachingEnabled") ?? false }
set { sharedDefaults?.set(newValue, forKey: "noCachingEnabled") }
}
public var isDNSSpoofingEnabled: Bool {
get { sharedDefaults?.bool(forKey: "dnsSpoofingEnabled") ?? false }
set { sharedDefaults?.set(newValue, forKey: "dnsSpoofingEnabled") }
}
public var hideSystemTraffic: Bool {
get { sharedDefaults?.bool(forKey: "hideSystemTraffic") ?? false }
set { sharedDefaults?.set(newValue, forKey: "hideSystemTraffic") }
}
}
// MARK: - Darwin Callback Storage
private final class DarwinCallbackStore: @unchecked Sendable {
static let shared = DarwinCallbackStore()
private var callbacks: [String: @Sendable () -> Void] = [:]
private let lock = NSLock()
func register(name: String, callback: @escaping @Sendable () -> Void) {
lock.lock()
callbacks[name] = callback
lock.unlock()
}
func fire(name: String) {
lock.lock()
let cb = callbacks[name]
lock.unlock()
cb?()
}
}