How to Add File Picker to the App on iOS 14+ and Lower

How to add file picker to the app on iOS 14+ and lower

As soon as you have file URL you can use that URL to retrieve the data it contains. When you have the data you can convert it to Base64 and send it to server. You gave no information about how you will send it to server but the rest may look something like this:

func sendFileWithURL(_ url: URL, completion: @escaping ((_ error: Error?) -> Void)) {
func finish(_ error: Error?) {
DispatchQueue.main.async {
completion(error)
}
}

DispatchQueue(label: "DownloadingFileData." + UUID().uuidString).async {
do {
let data: Data = try Data(contentsOf: url)
let base64String = data.base64EncodedString()
// TODO: send string to server and call the completion
finish(nil)
} catch {
finish(error)
}
}
}

and you would use it as

func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
urls.forEach { sendFileWithURL($0) { <#Your code here#> } }
}

To break it down:

To get file data you can use Data(contentsOf: url). This method even works on remote files so you could for instance use an URL of an image link anywhere on internet you have access to. It is important to know that this method will pause your thread which is usually not what you want.

To avoid breaking the current thread we create a new queue using DispatchQueue(label: "DownloadingFileData." + UUID().uuidString). The name of the queue is not very important but can be useful when debugging.

When data is received we convert it to Base64 string using data.base64EncodedString() and this data can then be sent to server. You just need to fill in the TODO: part.

Retrieving your file data can have some errors. Maybe access restriction or file no longer there or no internet connection... This is handled by throwing. If the statement with try fails for any reason then the catch parts executes and you receive an error.

Since all of this is done on background thread it usually makes sense to go back to main thread. This is what the finish function does. If you do not require that you can simply remove it and have:

func sendFileWithURL(_ url: URL, completion: @escaping ((_ error: Error?) -> Void)) {
DispatchQueue(label: "DownloadingFileData." + UUID().uuidString).async {
do {
let data: Data = try Data(contentsOf: url)
let base64String = data.base64EncodedString()
// TODO: send string to server and call the completion
completion(nil)
} catch {
completion(error)
}
}
}

There are other things to consider in this approach. For instance you can see if user selects multiple files then each of them will open its own queue and start the process. That means that if user selects multiple files it is possible that at some point many or all of them will be loaded in memory. That may take too much memory and crash your application. It is for you to decide if this approach is fine for you or you wish to serialize the process. The serialization should be very simple with queues. All you need is to have a single one:

private lazy var fileProcessingQueue: DispatchQueue = DispatchQueue(label: "DownloadingFileData.main")

func sendFileWithURL(_ url: URL, completion: @escaping ((_ error: Error?) -> Void)) {
func finish(_ error: Error?) {
DispatchQueue.main.async {
completion(error)
}
}

fileProcessingQueue.async {
do {
let data: Data = try Data(contentsOf: url)
let base64String = data.base64EncodedString()
// TODO: send string to server and call the completion
finish(nil)
} catch {
finish(error)
}
}
}

Now one operation will finish before another one starts. But that may only apply for getting file data and conversion to base64 string. If uploading is then done on another thread (Which usually is) then you may still have multiple ongoing requests which may contain all of the data needed to upload.

Implement Document Picker in swift (iOS)

Update for iOS 14: You do not need any capabilities. Just create a UIDocumentPickerViewController with the appropriate types, implement the delegate, and you are done.

More info in this answer. Code from there:


import UIKit
import MobileCoreServices
import UniformTypeIdentifiers

func selectFiles() {
let types = UTType.types(tag: "json",
tagClass: UTTagClass.filenameExtension,
conformingTo: nil)
let documentPickerController = UIDocumentPickerViewController(
forOpeningContentTypes: types)
documentPickerController.delegate = self
self.present(documentPickerController, animated: true, completion: nil)
}


From your project's capabilities, enable both the iCloud and the Key-Sharing.

Sample Image

Import MobileCoreServices in your class and then extend the following three classes inside your UIViewController:

UIDocumentMenuDelegate,UIDocumentPickerDelegate,UINavigationControllerDelegate

Implement the following functions:

public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard let myURL = urls.first else {
return
}
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 do you call all of this? Add the following bit of code to your click 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 ..

MenuPicker

In the case of Dropbox. Upon clicking on any item. You will be redirected back to your app and the URL will be logged in your terminal.

Sample Image

Manipulate the documentTypes to your need. In my app, Users permitted to Pdf only. So, suit yourself.

kUTTypePDF
kUTTypePNG
kUTTypeJPEG
...

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") })

Sample Image

Enjoy it.

SwiftUI - Use File Picker to Open a file belonging to another IOS Application, then access that file directly

So just for the archive, it turns out that my original Document Picker code was working - and was working correctly - just as I had provided it.

The problem turned out to be the way I was handling the response.

My original wrong code was:

do {
data1 = try NSData.init(
contentsOf: URL.init(fileURLWithPath: fileURL, isDirectory: true)
) as Data
}
catch {
print("Error \(error)")
}

and that was the problem. Turns out that to get the data stream from the file, all I needed to do was:

do {
data1 = try Data(contentsOf: fileURL!)
}
catch {
print("Error \(error)")
}

That was it! When I did that, I was able to read the contents of the file, and correctly upload them via POST to my server.

Thanks to all who read my question, and to Prafulla for their response!

Initialization of UIDocumentPickerViewController in iOS 14

Add import UniformTypeIdentifiers to your swift file to get access to UTType

See the documentation for more usages.

And the WWDC2020 video "Build document-based apps in SwiftUI" for a practical demo.

Showing a save file document picker in iOS without providing local file urls

iOS 13 offers means for picking directories (https://developer.apple.com/documentation/uikit/view_controllers/providing_access_to_directories). It is possible to create new items as well.

// Create a document picker for directories.
let documentPicker =
UIDocumentPickerViewController(documentTypes: [kUTTypeFolder as String],
in: .open)

SwiftUI fileImporter modifier: document picker shows up blank at first run

New development: this only happens when the initial document browser view is configured to show its content in COLUMNS mode - if in ICONS or LIST mode, works fine, the picker shows appropriately populated. I am scratching this one as a bug, already reported to Apple.

How to show file picker which pointed to application sandbox in ios?

Below is good library to create custom file picker which pointed to application sandbox (Document directory)

https://github.com/marmelroy/FileBrowser



Related Topics



Leave a reply



Submit