Created
October 28, 2025 14:38
-
-
Save wesmatlock/e6ff0bddb698e85a79f33216a37091b6 to your computer and use it in GitHub Desktop.
NetworkSecurityManager.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Foundation | |
| import CryptoKit | |
| import Network | |
| public final class NetworkSecurityManager { | |
| public static let shared = NetworkSecurityManager() | |
| private let monitor = NWPathMonitor() | |
| private let queue = DispatchQueue(label: "NetworkMonitor") | |
| private init() { | |
| setupNetworkMonitoring() | |
| } | |
| public func createSecureURLRequest(url: URL, | |
| method: String = "GET", | |
| body: Data? = nil) -> URLRequest { | |
| var request = URLRequest(url: url) | |
| request.httpMethod = method | |
| request.httpBody = body | |
| request.setValue("application/json", forHTTPHeaderField: "Content-Type") | |
| request.setValue(generateRequestSignature(), forHTTPHeaderField: "X-Request-Signature") | |
| request.setValue(UUID().uuidString, forHTTPHeaderField: "X-Request-ID") | |
| request.setValue(String(Date().timeIntervalSince1970), forHTTPHeaderField: "X-Timestamp") | |
| return request | |
| } | |
| private func generateRequestSignature() -> String { | |
| let timestamp = String(Date().timeIntervalSince1970) | |
| let nonce = UUID().uuidString | |
| let secretKey = "YOUR_SECRET_KEY" | |
| let signatureData = "\(timestamp):\(nonce):\(secretKey)".data(using: .utf8)! | |
| let hash = SHA256.hash(data: signatureData) | |
| return hash.compactMap { String(format: "%02x", $0) }.joined() | |
| } | |
| private func setupNetworkMonitoring() { | |
| monitor.pathUpdateHandler = { [weak self] path in | |
| self?.handleNetworkChange(path) | |
| } | |
| monitor.start(queue: queue) | |
| } | |
| private func handleNetworkChange(_ path: NWPath) { | |
| let isConstrained = path.isConstrained | |
| let isExpensive = path.isExpensive | |
| NotificationCenter.default.post( | |
| name: .networkConditionsChanged, | |
| object: nil, | |
| userInfo: [ | |
| "isExpensive": isExpensive, | |
| "isConstrained": isConstrained, | |
| "interface": path.availableInterfaces.first?.type.stringValue ?? "unknown" | |
| ] | |
| ) | |
| } | |
| } | |
| extension Notification.Name { | |
| static let networkConditionsChanged = Notification.Name("NetworkConditionsChanged") | |
| } | |
| private extension NWInterface.InterfaceType { | |
| var stringValue: String { | |
| switch self { | |
| case .wifi: return "wifi" | |
| case .cellular: return "cellular" | |
| case .wiredEthernet: return "wiredEthernet" | |
| case .loopback: return "loopback" | |
| case .other: return "other" | |
| @unknown default: return "unknown" | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment