From e9a1ce05557009ca8bf7976002a23a47fec6ff49 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 31 Jul 2025 02:21:18 +0200 Subject: [PATCH] warning fixes --- .../Core/Constants/NotificationNames.swift | 4 +- .../Core/Services/EventSource.swift | 21 +++--- .../Core/Services/NotificationService.swift | 65 ++++++++++--------- .../Core/Services/SessionMonitor.swift | 4 +- .../Services/SharedUnixSocketManager.swift | 8 +-- .../TailscaleServeStatusService.swift | 12 ++-- .../Settings/AuthenticationSection.swift | 14 ++-- .../Settings/NotificationSettingsView.swift | 21 +++--- .../Views/Settings/PermissionsSection.swift | 18 ++--- .../Settings/RemoteAccessSettingsView.swift | 4 +- mac/VibeTunnel/VibeTunnelApp.swift | 2 +- 11 files changed, 92 insertions(+), 81 deletions(-) diff --git a/mac/VibeTunnel/Core/Constants/NotificationNames.swift b/mac/VibeTunnel/Core/Constants/NotificationNames.swift index fd57def9..39a9c94c 100644 --- a/mac/VibeTunnel/Core/Constants/NotificationNames.swift +++ b/mac/VibeTunnel/Core/Constants/NotificationNames.swift @@ -13,9 +13,9 @@ extension Notification.Name { // MARK: - Welcome static let showWelcomeScreen = Notification.Name("showWelcomeScreen") - + // MARK: - Services - + static let notificationServiceConnectionChanged = Notification.Name("notificationServiceConnectionChanged") } diff --git a/mac/VibeTunnel/Core/Services/EventSource.swift b/mac/VibeTunnel/Core/Services/EventSource.swift index 52152167..bc942bba 100644 --- a/mac/VibeTunnel/Core/Services/EventSource.swift +++ b/mac/VibeTunnel/Core/Services/EventSource.swift @@ -60,9 +60,9 @@ final class EventSource: NSObject { // MARK: - Connection Management func connect() { - guard !isConnected else { + guard !isConnected else { logger.warning("Already connected, ignoring connect request") - return + return } var request = URLRequest(url: url) @@ -84,7 +84,7 @@ final class EventSource: NSObject { dataTask = urlSession?.dataTask(with: request) dataTask?.resume() - + logger.info("๐Ÿ“ก EventSource dataTask started") } @@ -136,7 +136,8 @@ final class EventSource: NSObject { } // Dispatch event - logger.debug("๐ŸŽฏ Dispatching event - type: \(event.event ?? "default"), data: \(event.data ?? "none")") + logger + .debug("๐ŸŽฏ Dispatching event - type: \(event.event ?? "default"), data: \(event.data ?? "none")") DispatchQueue.main.async { self.onMessage?(event) } @@ -197,7 +198,7 @@ extension EventSource: URLSessionDataDelegate { completionHandler: @escaping (URLSession.ResponseDisposition) -> Void ) { logger.info("๐Ÿ“ฅ URLSession didReceive response") - + guard let httpResponse = response as? HTTPURLResponse else { logger.error("Response is not HTTPURLResponse") completionHandler(.cancel) @@ -224,19 +225,19 @@ extension EventSource: URLSessionDataDelegate { func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { logger.debug("๐Ÿ“จ EventSource received \(data.count) bytes of data") - + // Check if data might be compressed if data.count > 2 { let header = [UInt8](data.prefix(2)) - if header[0] == 0x1f && header[1] == 0x8b { + if header[0] == 0x1F && header[1] == 0x8B { logger.error("โŒ Received gzip compressed data! SSE should not be compressed.") return } } - - guard let text = String(data: data, encoding: .utf8) else { + + guard let text = String(data: data, encoding: .utf8) else { logger.error("Failed to decode data as UTF-8. First 20 bytes: \(data.prefix(20).hexString)") - return + return } logger.debug("๐Ÿ“จ EventSource received text: \(text)") diff --git a/mac/VibeTunnel/Core/Services/NotificationService.swift b/mac/VibeTunnel/Core/Services/NotificationService.swift index a91e77be..f6885313 100644 --- a/mac/VibeTunnel/Core/Services/NotificationService.swift +++ b/mac/VibeTunnel/Core/Services/NotificationService.swift @@ -21,7 +21,7 @@ final class NotificationService: NSObject { private var isConnected = false private var recentlyNotifiedSessions = Set() private var notificationCleanupTimer: Timer? - + /// Public property to check SSE connection status var isSSEConnected: Bool { isConnected } @@ -67,13 +67,13 @@ final class NotificationService: NSObject { /// Start monitoring server events func start() async { logger.info("๐Ÿš€ NotificationService.start() called") - + // Check if notifications are enabled in config guard configManager.notificationsEnabled else { logger.info("๐Ÿ“ด Notifications are disabled in config, skipping SSE connection") return } - + guard serverManager.isRunning else { logger.warning("๐Ÿ”ด Server not running, cannot start notification service") return @@ -87,18 +87,18 @@ final class NotificationService: NSObject { waitForUnixSocketAndConnect() } } - + /// Wait for Unix socket ready notification then connect private func waitForUnixSocketAndConnect() { logger.info("โณ Waiting for Unix socket ready notification...") - + // Check if Unix socket is already connected if SharedUnixSocketManager.shared.isConnected { logger.info("โœ… Unix socket already connected, connecting to SSE immediately") connect() return } - + // Listen for Unix socket ready notification NotificationCenter.default.addObserver( forName: SharedUnixSocketManager.unixSocketReadyNotification, @@ -108,7 +108,7 @@ final class NotificationService: NSObject { Task { @MainActor [weak self] in self?.logger.info("โœ… Unix socket ready notification received, connecting to SSE") self?.connect() - + // Remove observer after first notification to prevent duplicate connections NotificationCenter.default.removeObserver( self as Any, @@ -423,7 +423,6 @@ final class NotificationService: NSObject { // This prevents dual-path connection attempts } - private func connect() { logger.info("๐Ÿ”Œ NotificationService.connect() called - isConnected: \(self.isConnected)") guard !isConnected else { @@ -434,13 +433,14 @@ final class NotificationService: NSObject { // When auth mode is "none", we can connect without a token. // In any other auth mode, a token is required for the local Mac app to connect. if serverManager.authMode != "none", serverManager.localAuthToken == nil { - logger.error("No auth token available for notification service in auth mode '\(self.serverManager.authMode)'") + logger + .error("No auth token available for notification service in auth mode '\(self.serverManager.authMode)'") return } let eventsURL = "http://localhost:\(self.serverManager.port)/api/events" logger.info("๐Ÿ“ก Attempting to connect to SSE endpoint: \(eventsURL)") - + guard let url = URL(string: eventsURL) else { logger.error("Invalid events URL: \(eventsURL)") return @@ -489,7 +489,10 @@ final class NotificationService: NSObject { eventSource?.onMessage = { [weak self] event in Task { @MainActor in - self?.logger.info("๐ŸŽฏ EventSource onMessage fired! Event type: \(event.event ?? "default"), Has data: \(event.data != nil)") + self?.logger + .info( + "๐ŸŽฏ EventSource onMessage fired! Event type: \(event.event ?? "default"), Has data: \(event.data != nil)" + ) self?.handleEvent(event) } } @@ -507,9 +510,9 @@ final class NotificationService: NSObject { } private func handleEvent(_ event: Event) { - guard let data = event.data else { + guard let data = event.data else { logger.warning("Received event with no data") - return + return } // Log event details for debugging @@ -725,25 +728,25 @@ final class NotificationService: NSObject { private func handleTestNotification(_ json: [String: Any]) { logger.info("๐Ÿงช Handling test notification from server") - + let title = json["title"] as? String ?? "VibeTunnel Test" let body = json["body"] as? String ?? "Server-side notifications are working correctly!" let message = json["message"] as? String - + let content = UNMutableNotificationContent() content.title = title content.body = body - if let message = message { + if let message { content.subtitle = message } content.sound = getNotificationSound() content.categoryIdentifier = "TEST" content.userInfo = ["type": "test-notification"] - + logger.info("๐Ÿ“ค Delivering test notification: \(title) - \(body)") deliverNotification(content, identifier: "test-\(UUID().uuidString)") } - + private func handleClaudeTurn(_ json: [String: Any]) { guard let sessionId = json["sessionId"] as? String else { logger.error("Claude turn event missing sessionId") @@ -803,13 +806,13 @@ final class NotificationService: NSObject { /// Send a test notification through the server to verify the full flow func sendServerTestNotification() async { logger.info("๐Ÿงช Sending test notification through server...") - + // Check if server is running guard serverManager.isRunning else { logger.error("โŒ Cannot send test notification - server is not running") return } - + // If not connected to SSE, try to connect first if !isConnected { logger.warning("โš ๏ธ Not connected to SSE endpoint, attempting to connect...") @@ -819,33 +822,36 @@ final class NotificationService: NSObject { // Give it a moment to connect try? await Task.sleep(nanoseconds: 500_000_000) // 0.5 seconds } - + // Log server info - logger.info("Server info - Port: \(self.serverManager.port), Running: \(self.serverManager.isRunning), SSE Connected: \(self.isConnected)") - + logger + .info( + "Server info - Port: \(self.serverManager.port), Running: \(self.serverManager.isRunning), SSE Connected: \(self.isConnected)" + ) + guard let url = serverManager.buildURL(endpoint: "/api/test-notification") else { logger.error("โŒ Failed to build test notification URL") return } - + logger.info("๐Ÿ“ค Sending POST request to: \(url)") - + var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") - + // Add auth token if available if let authToken = serverManager.localAuthToken { request.setValue("Bearer \(authToken)", forHTTPHeaderField: "Authorization") logger.debug("Added auth token to request") } - + do { let (data, response) = try await URLSession.shared.data(for: request) - + if let httpResponse = response as? HTTPURLResponse { logger.info("๐Ÿ“ฅ Received response - Status: \(httpResponse.statusCode)") - + if httpResponse.statusCode == 200 { logger.info("โœ… Server test notification sent successfully") if let responseData = String(data: data, encoding: .utf8) { @@ -870,4 +876,3 @@ final class NotificationService: NSObject { // NotificationCenter observers are automatically removed on deinit in modern Swift } } - diff --git a/mac/VibeTunnel/Core/Services/SessionMonitor.swift b/mac/VibeTunnel/Core/Services/SessionMonitor.swift index 113e9a9b..d0e09f11 100644 --- a/mac/VibeTunnel/Core/Services/SessionMonitor.swift +++ b/mac/VibeTunnel/Core/Services/SessionMonitor.swift @@ -134,7 +134,7 @@ final class SessionMonitor { private func fetchSessions() async { do { // Snapshot previous sessions for exit notifications - let _ = sessions + _ = sessions let sessionsArray = try await serverManager.performRequest( endpoint: APIEndpoints.sessions, @@ -233,4 +233,4 @@ final class SessionMonitor { "Pre-caching Git data for \(uniqueDirectoriesToCheck.count) unique directories (from \(sessions.count) sessions)" ) } -} \ No newline at end of file +} diff --git a/mac/VibeTunnel/Core/Services/SharedUnixSocketManager.swift b/mac/VibeTunnel/Core/Services/SharedUnixSocketManager.swift index 00e561c5..a7661ced 100644 --- a/mac/VibeTunnel/Core/Services/SharedUnixSocketManager.swift +++ b/mac/VibeTunnel/Core/Services/SharedUnixSocketManager.swift @@ -22,9 +22,9 @@ final class SharedUnixSocketManager { private init() { logger.info("๐Ÿš€ SharedUnixSocketManager initialized") } - + // MARK: - Notifications - + static let unixSocketReadyNotification = Notification.Name("unixSocketReady") // MARK: - Public Methods @@ -45,7 +45,7 @@ final class SharedUnixSocketManager { self?.distributeMessage(data) } } - + // Set up state change handler to notify when socket is ready socket.onStateChange = { [weak self] state in Task { @MainActor [weak self] in @@ -56,7 +56,7 @@ final class SharedUnixSocketManager { unixSocket = socket return socket } - + /// Handle socket state changes and notify when ready private func handleSocketStateChange(_ state: UnixSocketConnection.ConnectionState) { switch state { diff --git a/mac/VibeTunnel/Core/Services/TailscaleServeStatusService.swift b/mac/VibeTunnel/Core/Services/TailscaleServeStatusService.swift index 4284fbd2..1e14e6ad 100644 --- a/mac/VibeTunnel/Core/Services/TailscaleServeStatusService.swift +++ b/mac/VibeTunnel/Core/Services/TailscaleServeStatusService.swift @@ -74,11 +74,13 @@ final class TailscaleServeStatusService { let decoder = JSONDecoder() // Use custom date decoder to handle ISO8601 with fractional seconds - let formatter = ISO8601DateFormatter() - formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] decoder.dateDecodingStrategy = .custom { decoder in let container = try decoder.singleValueContainer() let dateString = try container.decode(String.self) + + // Create formatter inside the closure to avoid Sendable warning + let formatter = ISO8601DateFormatter() + formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] if let date = formatter.date(from: dateString) { return date } @@ -87,7 +89,10 @@ final class TailscaleServeStatusService { if let date = formatter.date(from: dateString) { return date } - throw DecodingError.dataCorruptedError(in: container, debugDescription: "Cannot decode date string \(dateString)") + throw DecodingError.dataCorruptedError( + in: container, + debugDescription: "Cannot decode date string \(dateString)" + ) } let status = try decoder.decode(TailscaleServeStatus.self, from: data) @@ -98,7 +103,6 @@ final class TailscaleServeStatusService { startTime = status.startTime logger.debug("Tailscale Serve status - Running: \(status.isRunning), Error: \(status.lastError ?? "none")") - } catch { logger.error("Failed to fetch Tailscale Serve status: \(error.localizedDescription)") // On error, assume not running diff --git a/mac/VibeTunnel/Presentation/Views/Settings/AuthenticationSection.swift b/mac/VibeTunnel/Presentation/Views/Settings/AuthenticationSection.swift index 9f188fcb..f6b2ad2c 100644 --- a/mac/VibeTunnel/Presentation/Views/Settings/AuthenticationSection.swift +++ b/mac/VibeTunnel/Presentation/Views/Settings/AuthenticationSection.swift @@ -1,6 +1,6 @@ -import SwiftUI import AppKit import os.log +import SwiftUI /// Authentication configuration section for remote access settings struct AuthenticationSection: View { @@ -8,7 +8,7 @@ struct AuthenticationSection: View { @Binding var enableSSHKeys: Bool let logger: Logger let serverManager: ServerManager - + var body: some View { Section { VStack(alignment: .leading, spacing: 16) { @@ -45,7 +45,7 @@ struct AuthenticationSection: View { .fixedSize(horizontal: false, vertical: true) .frame(maxWidth: .infinity, alignment: .leading) } - + // Additional info based on selected mode if authMode == .osAuth || authMode == .both { HStack(alignment: .center, spacing: 6) { @@ -59,7 +59,7 @@ struct AuthenticationSection: View { Spacer() } } - + if authMode == .sshKeys || authMode == .both { HStack(alignment: .center, spacing: 6) { Image(systemName: "key.fill") @@ -105,7 +105,7 @@ struct AuthenticationSection: View { struct PreviewWrapper: View { @State var authMode = AuthenticationMode.osAuth @State var enableSSHKeys = false - + var body: some View { AuthenticationSection( authMode: $authMode, @@ -117,6 +117,6 @@ struct AuthenticationSection: View { .padding() } } - + return PreviewWrapper() -} \ No newline at end of file +} diff --git a/mac/VibeTunnel/Presentation/Views/Settings/NotificationSettingsView.swift b/mac/VibeTunnel/Presentation/Views/Settings/NotificationSettingsView.swift index 76b53d04..64a8fb88 100644 --- a/mac/VibeTunnel/Presentation/Views/Settings/NotificationSettingsView.swift +++ b/mac/VibeTunnel/Presentation/Views/Settings/NotificationSettingsView.swift @@ -27,7 +27,7 @@ struct NotificationSettingsView: View { var body: some View { NavigationStack { @Bindable var bindableConfig = configManager - + Form { // Master toggle section Section { @@ -37,7 +37,7 @@ struct NotificationSettingsView: View { .onChange(of: showNotifications) { _, newValue in // Update ConfigManager's notificationsEnabled to match configManager.notificationsEnabled = newValue - + // Ensure NotificationService starts/stops based on the toggle if newValue { Task { @@ -63,7 +63,7 @@ struct NotificationSettingsView: View { Text("Display native macOS notifications for session and command events") .font(.caption) .foregroundStyle(.secondary) - + // SSE Connection Status Row HStack(spacing: 6) { Circle() @@ -78,10 +78,11 @@ struct NotificationSettingsView: View { .fontWeight(.medium) Spacer() } - .help(sseConnectionStatus - ? "Real-time notification stream is connected" - : "Real-time notification stream is disconnected. Check if the server is running.") - + .help(sseConnectionStatus + ? "Real-time notification stream is connected" + : "Real-time notification stream is disconnected. Check if the server is running." + ) + // Show warning when disconnected if showNotifications && !sseConnectionStatus { HStack(spacing: 6) { @@ -198,7 +199,7 @@ struct NotificationSettingsView: View { .scaleEffect(0.7) .frame(width: 16, height: 16) } - + Spacer() } @@ -207,7 +208,7 @@ struct NotificationSettingsView: View { notificationService.openNotificationSettings() } .buttonStyle(.link) - + Spacer() } } @@ -223,7 +224,7 @@ struct NotificationSettingsView: View { .onAppear { // Sync the AppStorage value with ConfigManager on first load showNotifications = configManager.notificationsEnabled - + // Update initial connection status sseConnectionStatus = notificationService.isSSEConnected } diff --git a/mac/VibeTunnel/Presentation/Views/Settings/PermissionsSection.swift b/mac/VibeTunnel/Presentation/Views/Settings/PermissionsSection.swift index 213b9f1b..0e639ee1 100644 --- a/mac/VibeTunnel/Presentation/Views/Settings/PermissionsSection.swift +++ b/mac/VibeTunnel/Presentation/Views/Settings/PermissionsSection.swift @@ -5,7 +5,7 @@ struct PermissionsSection: View { let hasAppleScriptPermission: Bool let hasAccessibilityPermission: Bool let permissionManager: SystemPermissionManager - + var body: some View { Section { // Automation permission @@ -17,9 +17,9 @@ struct PermissionsSection: View { .font(.caption) .foregroundStyle(.secondary) } - + Spacer() - + if hasAppleScriptPermission { HStack { Image(systemName: "checkmark.circle.fill") @@ -47,7 +47,7 @@ struct PermissionsSection: View { .controlSize(.small) } } - + // Accessibility permission HStack { VStack(alignment: .leading, spacing: 4) { @@ -57,9 +57,9 @@ struct PermissionsSection: View { .font(.caption) .foregroundStyle(.secondary) } - + Spacer() - + if hasAccessibilityPermission { HStack { Image(systemName: "checkmark.circle.fill") @@ -115,7 +115,7 @@ struct PermissionsSection: View { struct PreviewWrapper: View { @State var hasAppleScript = true @State var hasAccessibility = false - + var body: some View { PermissionsSection( hasAppleScriptPermission: hasAppleScript, @@ -126,6 +126,6 @@ struct PermissionsSection: View { .padding() } } - + return PreviewWrapper() -} \ No newline at end of file +} diff --git a/mac/VibeTunnel/Presentation/Views/Settings/RemoteAccessSettingsView.swift b/mac/VibeTunnel/Presentation/Views/Settings/RemoteAccessSettingsView.swift index 30270e44..d2a4b4f4 100644 --- a/mac/VibeTunnel/Presentation/Views/Settings/RemoteAccessSettingsView.swift +++ b/mac/VibeTunnel/Presentation/Views/Settings/RemoteAccessSettingsView.swift @@ -94,7 +94,8 @@ struct RemoteAccessSettingsView: View { onAppearSetup() updateLocalIPAddress() // Initialize authentication mode from stored value - let storedMode = UserDefaults.standard.string(forKey: AppConstants.UserDefaultsKeys.authenticationMode) ?? "os" + let storedMode = UserDefaults.standard + .string(forKey: AppConstants.UserDefaultsKeys.authenticationMode) ?? "os" authMode = AuthenticationMode(rawValue: storedMode) ?? .osAuth // Start monitoring Tailscale Serve status tailscaleServeStatus.startMonitoring() @@ -628,7 +629,6 @@ private struct ErrorView: View { } } - // MARK: - Previews #Preview("Remote Access Settings") { diff --git a/mac/VibeTunnel/VibeTunnelApp.swift b/mac/VibeTunnel/VibeTunnelApp.swift index b232bb0b..e630503b 100644 --- a/mac/VibeTunnel/VibeTunnelApp.swift +++ b/mac/VibeTunnel/VibeTunnelApp.swift @@ -299,7 +299,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser statusBarController?.updateStatusItemDisplay() // Session monitoring starts automatically - + // NotificationService is started by ServerManager when the server is ready } else { logger.error("HTTP server failed to start")