Implement Document Picker in Swift (Ios)

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.

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.

iOS Document Picker crashes when picking a PDF on a real device

The issue there is that you are using the wrong mode when defining the type of file transfer operation used by the document picker. .open is used to open an external file located outside your app’s sandbox. What you need is to use .import it will create a temporary file that will allow you to load its contents or move/copy the file to a permanent location. If it doesn't solve you issue check this post on how to implement your DocumentPickerViewController coordinator

let controller = UIDocumentPickerViewController(documentTypes: [String(kUTTypePDF)], in: .import)

Swift 5 - documentPicker not called after a file was selected

You need to retain the document-picker delegate (in this case, the instance of ImportMenuController).

This fixes it for you.

let importer = ImportMenuController()
var body: some View {
ZStack() {
Button("Select files to sync") {
importer.selectFile()
}
}

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!

Select two documents using iOS Document Picker in Swift

Selecting multiple files will be possible with iOS11 by setting “allowMultipleSelection” Boolean to true for your UidocumentPicker https://developer.apple.com/documentation/uikit/uidocumentpickerviewcontroller/2902365-allowsmultipleselection

How to open the Document files e.g(.pdf,.doc,.docx) in ios mobile when a button action using swift3.0?

Swift 3*, 4*,

To open document and select any document, you are using UIDocumentPickerViewController then all documents presented in your iCloud, Files and in Google Drive will be shown if Google Drive is connected in user device. Then selected document need to download in your app and from there you can show it in WKWebView,

   @IBAction func uploadNewResumeAction(_ sender: Any) {

/* let documentPicker = UIDocumentPickerViewController(documentTypes: ["com.apple.iwork.pages.pages", "com.apple.iwork.numbers.numbers", "com.apple.iwork.keynote.key","public.image", "com.apple.application", "public.item","public.data", "public.content", "public.audiovisual-content", "public.movie", "public.audiovisual-content", "public.video", "public.audio", "public.text", "public.data", "public.zip-archive", "com.pkware.zip-archive", "public.composite-content", "public.text"], in: .import) */

let documentPicker = UIDocumentPickerViewController(documentTypes: ["public.text", "com.apple.iwork.pages.pages", "public.data"], in: .import)

documentPicker.delegate = self
present(documentPicker, animated: true, completion: nil)
}

extension YourViewController: UIDocumentPickerDelegate{


func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) {

let cico = url as URL
print(cico)
print(url)

print(url.lastPathComponent)

print(url.pathExtension)

}
}

Note: If you intend to select all files the you have to use following code:

  let documentPicker = UIDocumentPickerViewController(documentTypes: ["com.apple.iwork.pages.pages", "com.apple.iwork.numbers.numbers", "com.apple.iwork.keynote.key","public.image", "com.apple.application", "public.item","public.data", "public.content", "public.audiovisual-content", "public.movie", "public.audiovisual-content", "public.video", "public.audio", "public.text", "public.data", "public.zip-archive", "com.pkware.zip-archive", "public.composite-content", "public.text"], in: .import) 

In your action method.

Does using UIDocumentPickerViewController need iCloud capability to be enabled?

No, you do not need iCloud capability to be enabled for UIDocumentPickerViewController. It doesn't require any capabilities actually.



Related Topics



Leave a reply



Submit