Reputation: 9743
I am using the .fileImporter modifier in SwiftUI to import pdf files in my app. I'm having a few issues. Firstly the data is slow to load and often it doesn't load and gives message Internal Error Couldn't communicate with a helper application.
But the main issue is if the modal view is dismissed with a swipe down, it cannot be presented again. Presumably because the binding $showFileImporter is not reset. If the cancel button is pressed to dismiss it works fine. I don't know if there is anyway to force it to fullScreen to get around this.
Here is my code:
.fileImporter(isPresented: $showFileImporter, allowedContentTypes: [.pdf]) { result in
switch result {
case .success(let url):
url.startAccessingSecurityScopedResource()
if let pDFDocument = PDFDocument(url: url) {
if let data = pDFDocument.dataRepresentation() {
// handle data
}
}
case .failure(let error):
print(error)
}
}
Upvotes: 4
Views: 1699
Reputation: 141
For anyone looking for this after 2024:
You can use the .onChange modifier to determine if the picker was dismissed.
Example:
.fileImporter(isPresented: $isImporting, allowedContentTypes: [.mp3, .wav, .midi], onCompletion: {...}
.onChange(of: isImporting) { oldValue, newValue in
print("Picker state = \(oldValue) -> \(newValue)")
}
Upvotes: 0
Reputation: 1542
The issue persists as of August 2022. I needed to implement Joseph Dvorak's most excellent fix in multiple places so abstracted it on to any equatable binding:
// Usage
$showFileImporter.trampolineIfNeeded(to: true)
// Implementation
extension Binding where Value: Equatable {
func trampolineIfNeeded(to: Value, via: Value) {
if wrappedValue == to {
wrappedValue = via
DispatchQueue.main.asyncAfter(deadline: .now()+0.2, execute: {
wrappedValue = to
})
} else {
wrappedValue = to
}
}
}
extension Binding where Value == Bool {
func trampolineIfNeeded(to: Value) {
trampolineIfNeeded(to: to, via: !to)
}
}
Upvotes: 0
Reputation: 356
I can confirm my .fileImporter also does not reset the isPresented binding on swipe-down. One way to be for certain is to change your button that presents the file importer from $showFileImporter = true
to $showFileImporter.toggle()
. On swiping to dismiss the file importer, you have to press the button twice to present again.
Unfortunately this seems to be yet another half-baked SwiftUI thing, but I did find a suitable work-around - handling this case with the presenting button itself:
Button(action: {
if showFileImporter {
// NOTE: Fixes broken fileimporter sheet not resetting on swipedown
showFileImporter = false
DispatchQueue.main.asyncAfter(deadline: .now()+0.2, execute: {
showFileImporter = true
})
} else {
showFileImporter = true
}
}, label: { ... })
I tried just calling .toggle() twice in a row sequentially but needed a slight delay for the trigger to register.
Edit:
I also noticed you didn't call url.stopAccessingSecurityScopedResource()
, which you should do after calling to your pDFDocument
.
Upvotes: 12