Merge pull request #728 from hernan0078/ios-picker-dismiss

iOS: a file picker dismissed by swiping locks out every later picker
This commit is contained in:
bryanthaboi
2026-08-03 09:43:02 -04:00
committed by GitHub
+31 -4
View File
@@ -174,15 +174,27 @@ public final class GRPickerBridge: NSObject {
// Without this the stacked present makes the picker auto-dismiss
// with zero documents (observed as didPickDocumentsAt 0 urls)
// and the user's pick silently does nothing.
guard var top = UIApplication.shared.windows
.first(where: { $0.isKeyWindow })?.rootViewController
else { return false }
// Self-heal before consulting the list. If UIKit is presenting
// nothing at all, any delegate still in it belongs to a sheet
// that is long gone, and treating it as live would lock the
// picker out for the rest of the session. Belt and braces with
// the dismissal callback above: that one closes the known hole,
// this one closes whatever hole iOS invents next.
if top.presentedViewController == nil, !liveDelegates.isEmpty {
NSLog("GRPickerBridge: clearing %d stale delegate(s)",
liveDelegates.count)
liveDelegates.removeAll()
}
guard liveDelegates.isEmpty else {
NSLog("GRPickerBridge: picker already active; ignoring re-present")
return true
}
guard var top = UIApplication.shared.windows
.first(where: { $0.isKeyWindow })?.rootViewController
else { return false }
while let presented = top.presentedViewController { top = presented }
picker.delegate = delegate
picker.presentationController?.delegate = delegate
liveDelegates.append(delegate)
delegate.onFinish = { [weak delegate] in
liveDelegates.removeAll { $0 === delegate }
@@ -197,7 +209,8 @@ public final class GRPickerBridge: NSObject {
}
}
private final class PickerDelegate: NSObject, UIDocumentPickerDelegate {
private final class PickerDelegate: NSObject, UIDocumentPickerDelegate,
UIAdaptivePresentationControllerDelegate {
private let onPick: ([URL]) -> Void
var onFinish: (() -> Void)?
init(onPick: @escaping ([URL]) -> Void) { self.onPick = onPick }
@@ -215,4 +228,18 @@ private final class PickerDelegate: NSObject, UIDocumentPickerDelegate {
NSLog("GRPickerBridge: picker cancelled")
onFinish?()
}
// Swiping the sheet down calls NEITHER of the two above: since iOS 13 an
// interactively dismissed picker reports only through the adaptive
// presentation delegate. Without this the delegate is never taken out of
// liveDelegates, the re-present guard below then swallows every later
// picker while still answering true -- so Lua arms its poll and waits for
// a file that no sheet is ever going to produce. That is the whole of the
// "Import ROM does nothing until you restart the app" report: the restart
// is not refreshing anything, it is clearing this array.
func presentationControllerDidDismiss(_ presentationController:
UIPresentationController) {
NSLog("GRPickerBridge: picker dismissed interactively")
onFinish?()
}
}