tauri-apps / tauri-apps/plugins-workspace
[bug] iOS dialog (message/confirm/ask) not working on physical devices in release builds
- Dominant language
- Rust
- Stars
- 1.8k
- Forks
- 602
- Avg merge
- 4d 14h
- Merged PRs (30d)
- 9
Description
### Describe the problem
On iOS physical devices, **message**, **confirm**, and **ask** dialogs from `@tauri-apps/plugin-dialog` do not appear in **release builds**, though they work correctly in:
- ✅ Development builds on physical devices
- ✅ iOS Simulator (both dev and release builds)
The issue appears to be related to how the plugin obtains the view controller. The root cause is that `manager.viewController` returns `nil` in release builds on physical devices.
**Symptoms:**
- Dialog methods (`message()`, `confirm()`, `ask()`) are called but no dialog appears
- No error messages in console
- Promise hangs indefinitely (never resolves)
- App becomes unresponsive waiting for dialog response
**Reproduction:**
1. Build the app for iOS in **release mode**
2. Deploy to a **physical iOS device** (iPhone/iPad)
3. Call any dialog function:
import { message } from '@tauri-apps/plugin-dialog';
await message('Test message', { title: 'Test' });
4. Observe that no dialog appears and the promise never resolves
**Environment:**
- iOS: 15.7.9 (and likely other versions)
- Tauri: 2.9.4
- Plugin version: `@tauri-apps/plugin-dialog` latest
- Device: Physical iPhone (not simulator)
- Build type: Release only (dev builds work fine)
#### Describe the solution you'd like
The plugin should use `UIApplication.shared.connectedScenes` to obtain the root view controller instead of relying on `manager.viewController`, which is `nil` in release builds.
#### Proposed fix (Swift):
```
private func getRootViewController() -> UIViewController? {
guard let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
return nil
}
let window = scene.windows.first(where: { $0.isKeyWindow }) ?? scene.windows.first
return window?.rootViewController
}
```
This approach:
- ✅ Works reliably in all iOS configurations (dev/release, simulator/device)
- ✅ Uses modern iOS scene-based architecture (iOS 13+)
- ✅ Properly handles multi-window scenarios
- ✅ Returns the active window's root view controller
#### Workaround implemented
We've created a custom plugin for iOS-only that implements this fix. The plugin correctly obtains the view controller using `UIApplication.shared.connectedScenes` and works in all configurations.
##### Key differences from original plugin:
1. View Controller Access:
- ❌ Original: Uses manager.viewController (returns nil in release builds)
- ✅ Workaround: Uses UIApplication.shared.connectedScenes (always works)
2. Implementation:
```
DispatchQueue.main.async { [weak self] in
guard let rootVC = self?.getRootViewController() else {
invoke.reject("Root view controller not found")
return
}
let alert = UIAlertController(...)
rootVC.present(alert, animated: true)
}
```
The fix is straightforward and only requires changing how the view controller is obtained.
#### Related
This issue is specific to message/confirm/ask dialogs, distinct from file picker issues like #3030.
##### Additional context:
- Safari Web Inspector shows request sent but no response received
- Network layer works correctly (IPC is functional)
- Issue is purely in the Swift/iOS native layer
Would appreciate if this fix could be incorporated into the official plugin to benefit all Tauri iOS developers facing this issue!
Contributor guide
Assessment
This issue has not been assessed yet.