UIDocumentPickerViewController allows selecting a file the first time the app is run, but not subsequently - swift

My app, when run, creates a json document (using a UIDocument subclass) in its document directory. And then, when opening up the UIDocumentPickerViewController to select a file, if the app has written a new file, the behaviour is as expected.
However, if I run the app again (and overwrite the last created file), the delegate method didPickDocumentsAt doesn't get called, unless I browse around for a few seconds.
What am I missing here?
#IBAction func showDocumentPicker() {
let documentPicker = UIDocumentPickerViewController(documentTypes: [kUTTypeJSON as String], in: .import)
documentPicker.allowsMultipleSelection = false
documentPicker.delegate = self
self.present(documentPicker, animated: true, completion: nil)
} //this function is in the initial definition of the class and is connected to a UIBarButton
extension BudgetExportViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
print("selected document: \(urls.first)")
print("555555555555555555555555555555")
//document = BudgetExportDocument(fileURL: urls.first!)
// CFURLStartAccessingSecurityScopedResource(urls.first! as CFURL)
// let documentData = try? Data.init(contentsOf: urls.first!)
// let json = try? JSONDecoder().decode(BudgetExportData.self, from: documentData!)
// budgetThisMonth = json
// print("Budgetthismonth")
// print(budgetThisMonth)
// CFURLStopAccessingSecurityScopedResource(urls.first! as CFURL)
}
}

Apparently, the problem was the fact that I was overwriting the same file so many times. Now, if a file with the same name exists, it won't write over it and the UIDocumentPickerViewController behaviour is as expected.

Related

Cannot find where to place files for the Xcode simulator on my Mac

I am having trouble finding where to place files on my Mac so that my Xcode simulator sees them.
Working on a "file upload" section for my app. Before I call the UIDocumentPickerViewController, I do call my own function printSimDir which I use to open the proper folder on my Mac so I can throw my files in there.
And in there I have three files: "blank_inv, invoice_001.cvs, and example.mp3"
However, in my simulator, I don't see these files. I do however keep seeing one xls file that is not any of three above files. So at one point I did get this right. But not anymore.
I realize that my problem might also be in how I am calling the UIDocumentPickerViewController so am including that code as well.
case ButtType.file.rawValue:
printSimDir()
let supportedTypes: [UTType] = [UTType.spreadsheet, UTType.commaSeparatedText, .mp3]
let pickerViewController = UIDocumentPickerViewController(forOpeningContentTypes: supportedTypes)
pickerViewController.delegate = self
pickerViewController.allowsMultipleSelection = false
present(pickerViewController, animated: true, completion: nil)
...
extension UploadInv: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
for url in urls {
guard url.startAccessingSecurityScopedResource() else {
print ("error")
return
}
xFile = XFile(fileUrl: url, key: "filename")
myStartUPButt.isEnabled = true
do { url.stopAccessingSecurityScopedResource() }
myStatus.text = xFile?.filename
}
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
controller.dismiss(animated: true, completion: nil)
}
}
printSimDir()
func printSimDir(){
// tried the commented code as well
// let fManager = FileManager.default
// guard let url = fManager.urls(for: .documentDirectory, in: .userDomainMask).first else {return}
// print ("\(url)")
#if targetEnvironment(simulator)
if let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.path {
print("Documents Directory: \(documentsPath)")
}
#endif
}
For those who come across this. Not sure where I found the answer, but it's quite simple.
Simply drag and drop the files you want from your Mac to the Xcode
simulator's Home Screen. Currently have only tried them one at a time.
Is this the proper answer? I do not know. But it does work.
Haven't tried every way to do this, (one at a time, multiple files at once, multiple simulators, etc.) But even after a Mac restart the files are still there.

Swift Document Picker, can not reach the file

I am writing a project which involves picking a file and getting the content of the file. However, I think it is not reaching the correct url of the file.
Here is the function where it calls the document picker. It is activated by a button.
#IBAction func selectFile(_ sender: Any) {
let documentPicker = UIDocumentPickerViewController(documentTypes: [kUTTypePlainText as String], in: .import)
documentPicker.delegate = self
documentPicker.allowsMultipleSelection = false
present(documentPicker, animated: true, completion: nil)
}
And here is the extension to UIDocumentPickerViewController
extension ViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
print(urls)
do {
let fileContent = try String(contentsOf: urls[0], encoding: .utf8)
print(fileContent)
} catch {
return
}
}
}
In the console output, fileContent is not being printed out, instead, here is what it prints out.
Failed to associate thumbnails for picked URL
file:///Users/<user>/Library/Developer/CoreSimulator/Devices/480A2D02-810F-435E-BF44-4B2F6FC614A9/data/Containers/Data/Application/BAA825D0-C4D8-4B33-AFB3-3737ADCA0B29/Documents/test2.txt with the Inbox copy
file:///Users/<user>/Library/Developer/CoreSimulator/Devices/480A2D02-810F-435E-BF44-4B2F6FC614A9/data/Containers/Data/Application/BAA825D0-C4D8-4B33-AFB3-3737ADCA0B29/tmp/<project>-Inbox/test2.txt:
Error Domain=QLThumbnailErrorDomain Code=102 "(null)"
UserInfo={NSUnderlyingError=0x600003348060
{Error Domain=GSLibraryErrorDomain Code=3 "Generation not found" UserInfo={NSDescription=Generation not found}}}
There aren't many resources about this online, can someone help look at what I did wrong here?
Actually I made a small change on the UIDocumentPickerViewController initialiser. Adding the updated code below.
#IBAction func selectFile(_ sender: Any) {
let documentPicker = UIDocumentPickerViewController(documentTypes: ["public.text"], in: .import)
documentPicker.delegate = self
documentPicker.allowsMultipleSelection = false
present(documentPicker, animated: true, completion: nil) }
Rest of the code same, only replaced this ["public.text"] instead of [kUTTypePlainText as String].
May be it resolves your problem. Please let me know the feedback.
Credit gose to adrian check this link using-uidocumentpickerviewcontroller-to-import-text-in-swift
I know this is an old question, but if it is of help to anyone, I fixed the console issue by changing the instance type of UIDocumentPickerViewController, entering .open instead of .import
In my case, the behaviour does not change and I can choose a file without losing the expected behaviour.

Cocoa: How to re-read file content after file has been changed from another app using NSDocument?

I have a document based cocoa app that opens an .md file to display the markdown content in a nice format. If I change the .md file in another app like textedit, I want to reload the views in my app.
Here's what I have working so far:
import Cocoa
class Document: NSDocument {
var fileContent = "Nothing yet :("
override init() {
// Add your subclass-specific initialization here.
super.init()
}
override class var autosavesInPlace: Bool {
return false
}
override func makeWindowControllers() {
// Returns the Storyboard that contains your Document window.
let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil)
let windowController = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("Document Window Controller")) as! NSWindowController
self.addWindowController(windowController)
}
override func data(ofType typeName: String) throws -> Data {
throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
}
override func read(from data: Data, ofType typeName: String) throws {
fileContent = (try String(data: data, encoding: .utf8))!
}
// this fn is called every time textEdit changes the file content.
override func presentedItemDidChange() {
// Here is the PROBLEM:
// HOW do I access the new file content?
}
}
Here is the problem
presentedItemDidChange() is called every time textEdit makes a change. That works great. But I can't for the life of me figure out how then to access the new file content, so I can reassign fileContent = newContent. Any thoughts?
I would call for the document readFromURL:ofType:error: as described here.

iCloud Drive Issue: "[DocumentManager] Failed to associate thumbnails for picked URL"

I've created a JSON string file from an object containing multiple properties. This is the object:
RecipeFile : Codable {
var name: String
var theRecipeIngredients: [String]
var theRecipeSteps: [String]
var theRecipeRating: Int
var theRecipeCategory: String
var theRecipeIndexStrings: String
var theRecipeImage: String?
I create the JSON string file with this code:
let json_encoder = JSONEncoder()
let recipeFileName = recipeToDisplay.name! + UUID().uuidString + ".json"
let exportFilePath = getDocumentsDirectory().appendingPathComponent(recipeFileName)
do {
let jsonData = try json_encoder.encode(exportRecipeFile)
if let jsonString = String(data: jsonData, encoding: .utf8)
{
try jsonString.write(to: exportFilePath, atomically: false, encoding: .utf8)
}
} catch {
print(error.localizedDescription)
}
I upload it to iCloud Drive. I import the string file from iCloud Drive using UIDocumentPickerViewController. I can parse the imported file just fine. However, I get this message (edited to remove some path info) in the xCode debug area when func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) is called:
[DocumentManager] Failed to associate thumbnails for picked URL
file:///....Bourbon%20Chocolate%20Walnut%20Pie18D20181-DAFD-499C-9873-7D3E0794C37A.json
with the Inbox copy
file:///....Bourbon%20Chocolate%20Walnut%20Pie18D20181-DAFD-499C-9873-7D3E0794C37A.json:
Error Domain=QLThumbnail Code=2 "(null)"
UserInfo={NSUnderlyingError=0x149a042b0 {Error
Domain=GSLibraryErrorDomain Code=3 "Generation not found"
UserInfo={NSDescription=Generation not found}}}
Any idea what is causing this to be generated?
The didPickDocumentsAt code starts as follows:
let data = try? Data(contentsOf: urls[0]) as Data
let json_decoder = JSONDecoder()
do {
let importRecipeFile = try json_decoder.decode(RecipeFile.self, from: data!)
let importedRecipeToSave = Recipe(context: theMOC)
importedRecipeToSave.name = importRecipeFile.name
importedRecipeToSave.category = importRecipeFile.theRecipeCategory
importedRecipeToSave.rating = Int16(importRecipeFile.theRecipeRating)
importedRecipeToSave.terms = importRecipeFile.theRecipeIndexStrings
importedRecipeToSave.addedToGroceryList = false
You can safely ignore this message. When you import a file from iCloud, iOS tries to copy the thumbnail from iCloud to the imported copy, but for JSON files there's no thumbnail to copy and it logs this. This is not an error on your side.
I can reproduce this issue with SwiftUI with iOS 14. After Originally I present the UIDocumentPickerViewController after a ToolbarItem pressed and it works fine. Later I refactor the UI and the View is pushed from other parent View and this error occurs and the JSON is not received.
The same thing has happened to me, and I have not found a concrete solution in any forum. But, by testing and mixing code that I found in the forums finally worked for me. Still, I don't know exactly what's wrong.
I leave my code here in case it's useful for someone, although it's been many months since this question.
func importarCsv(sender: UIBarButtonItem) {
let types = [kUTTypePDF,kUTTypeUTF8PlainText]
let importMenu = UIDocumentPickerViewController(documentTypes: types as [String], in: .import)
if #available(iOS 11.0, *) {
importMenu.allowsMultipleSelection = false
}
importMenu.delegate = self
present(importMenu, animated: true, completion: nil)
}
extension MaterialViewController: UIDocumentPickerDelegate {
internal func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt urls: URL) {
print("urls : \(urls)")
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
print("close")
controller.dismiss(animated: true, completion: nil)
}
}
I had this problem when i presented the UIDocumentPicker from another UIViewController before adding it as a child view controller on its parent view controller.

DropBox Chooser and documentsPicker for Swift developers 3.0

While the Chooser implementations for iOS are present Here. It is however limited to Objective-C. Is it possible to create a chooser in swift manually?
(A dropBox chooser)
I am also unable to sufficiently call the documentspicker functions, where one can pull any document from any app the user may have installed.
Thank you in advance
ā­Solved
From your project's capabilites. First enable both the iCloud serivces and the key Sharing, now import MobileCoreServices in your class. Finally extended the following three classes inside your class.
UIDocumentMenuDelegate,UIDocumentPickerDelegate,UINavigationControllerDelegate
Now implement the following functions..
public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) {
let myURL = url as URL
print("import result : /(myURL)")
}
public func documentMenu(_ documentMenu:UIDocumentMenuViewController, didPickDocumentPicker documentPicker: UIDocumentPickerViewController) {
documentPicker.delegate = self
present(documentPicker, animated: true, completion: nil)
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
print("view was cancelled")
dismiss(animated: true, completion: nil)
}
How to call all of this? Add the following bit of code to your clickable function..
func clickFunction(){
let importMenu = UIDocumentMenuViewController(documentTypes: [String(kUTTypePDF)], in: .import)
importMenu.delegate = self
importMenu.modalPresentationStyle = .formSheet
self.present(importMenu, animated: true, completion: nil)
}
Click your button. The following menu will pop up ..
In the case of DropBox. Upon clicking on any item. You will be redirected to your app. And the Url will be printed.
Manipulate the documentTypes to your need. In my app, Users permitted to Pdf only. So, suit yourself.
kUTTypePDF
Also if you feel like customizing your own menu bar. Add the following code and customize your own function inside the handler
importMenu.addOption(withTitle: "Create New Document", image: nil, order: .first, handler: { print("New Doc Requested") })
Enjoy it.