From 4b00130b86a27b6f121bf57897b5e92d83bcc0fc Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Thu, 13 Nov 2025 08:18:50 -0300 Subject: [PATCH] refactor(core): improve iOS log messages from stdout/stderr (#14385) * refactor(core): improve iOS log messages from stdout/stderr move the stdout/stderr forward logic to Swift so it does not consume a Rust thread and never deadlocks on the simulator I had to work on this because i'm facing #12172 again on latest Xcode (26.1) * patch --- .changes/refactor-ios-stdout-stderr.md | 6 ++ .../mobile/ios-api/Sources/Tauri/Logger.swift | 83 ++++++++++++++++++- .../mobile/ios-api/Sources/Tauri/Tauri.swift | 8 ++ crates/tauri/src/ios.rs | 1 + crates/tauri/src/lib.rs | 32 +------ 5 files changed, 99 insertions(+), 31 deletions(-) create mode 100644 .changes/refactor-ios-stdout-stderr.md diff --git a/.changes/refactor-ios-stdout-stderr.md b/.changes/refactor-ios-stdout-stderr.md new file mode 100644 index 000000000..766cd9fb5 --- /dev/null +++ b/.changes/refactor-ios-stdout-stderr.md @@ -0,0 +1,6 @@ +--- +"tauri": patch:bug +"tauri-macros": patch:bug +--- + +Fix iOS deadlock when running on the simulator from Xcode by properly piping stdout/stderr messages through the Xcode console and OSLog. diff --git a/crates/tauri/mobile/ios-api/Sources/Tauri/Logger.swift b/crates/tauri/mobile/ios-api/Sources/Tauri/Logger.swift index 9fa6e3fe7..d055090dc 100644 --- a/crates/tauri/mobile/ios-api/Sources/Tauri/Logger.swift +++ b/crates/tauri/mobile/ios-api/Sources/Tauri/Logger.swift @@ -4,6 +4,86 @@ import os.log import UIKit +import Foundation + +class StdoutRedirector { + private var originalStdout: Int32 = -1 + private var originalStderr: Int32 = -1 + private var stdoutPipe: [Int32] = [-1, -1] + private var stderrPipe: [Int32] = [-1, -1] + private var stdoutReadSource: DispatchSourceRead? + private var stderrReadSource: DispatchSourceRead? + + func start() { + originalStdout = dup(STDOUT_FILENO) + originalStderr = dup(STDERR_FILENO) + + guard Darwin.pipe(&stdoutPipe) == 0, + Darwin.pipe(&stderrPipe) == 0 else { + Logger.error("Failed to create stdout/stderr pipes") + return + } + + dup2(stdoutPipe[1], STDOUT_FILENO) + dup2(stderrPipe[1], STDERR_FILENO) + close(stdoutPipe[1]) + close(stderrPipe[1]) + + stdoutReadSource = createReader( + readPipe: stdoutPipe[0], + writeToOriginal: originalStdout, + label: "stdout" + ) + + stderrReadSource = createReader( + readPipe: stderrPipe[0], + writeToOriginal: originalStderr, + label: "stderr" + ) + } + + private func createReader( + readPipe: Int32, + writeToOriginal: Int32, + label: String + ) -> DispatchSourceRead { + let source = DispatchSource.makeReadSource( + fileDescriptor: readPipe, + queue: .global(qos: .utility) + ) + + source.setEventHandler { + let bufferSize = 4096 + var buffer = [UInt8](repeating: 0, count: bufferSize) + let bytesRead = read(readPipe, &buffer, bufferSize) + + if bytesRead > 0 { + let output = String( + bytes: buffer[0..