74

I want to pick a file of any type(.pdf, .docs, .xlsx, .jpeg, .txt, .rtf, etc) functionality in my iOS app. On clicking on Upload button, I want my app to open a directory and select files(DocumentsPicker)

@IBAction pickDocument(sender: UIButton) {
    //Open Document Picker
}

Any approach to do so in Swift?

  • 1
    iOS apps are sandboxed so you can't open any directory outside of your apps sandbox. Also you question is way to broad and does not show any attempt where you tried to solve this functionality yourself. – rckoenes May 18 '16 at 10:38
  • @Nilesh Pol : What if I have to select files from here and upload it to server. Do I have to download it and then send via multipart or is there any other way to do so? Please suggest. – Ishika Mar 08 '18 at 06:46
  • @Ishika document picker download it for you.You don't need to do that by yourself. – Surjeet Rajput Mar 14 '18 at 11:59

10 Answers10

116

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.

enter image description here

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.

enter image description here

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

enter image description here

Enjoy it.

fabian789
  • 8,348
  • 4
  • 45
  • 91
Khaledonia
  • 2,054
  • 3
  • 15
  • 33
  • 1
    cannot set to pdf only with "public.pdf " showing this error "Passed in type public.pdf doesn't conform to either public.content or public.item. If you are exporting a new type, please ensure that it conforms to an appropriate parent type." – Subhojit Mandal Jun 14 '17 at 20:35
  • 1
    why i am getting no location available – Rohit Dhawan Jul 17 '17 at 06:56
  • 1
    @Alexander how can we add custom images that do not use the default tint colour and do not go outside the bounds of the available icon size? – user2363025 Jan 09 '18 at 13:59
  • 1
    @Alexander the only way I can get it to fit is to reduce the size of my icon to 25 x 25 but then it is very pixelated – user2363025 Jan 10 '18 at 09:23
  • 1
    how to view a file after picking from desired place? – Deepakraj Murugesan Jan 24 '18 at 03:58
  • @DeepakrajMurugesan you can `loadRequest(NSURLRequest(url: ) as URLRequest)` – Khaledonia Jan 24 '18 at 06:23
  • 3
    @Alexander What if I have to select files from here and upload it to server. Do I have to download it and then send via multipart or is there any other way to do so? – Ishika Mar 07 '18 at 07:28
  • @RaviPadsala import MobileCoreServices inside your ViewController – Khaledonia Aug 29 '18 at 07:16
  • 1
    @DeepakrajMurugesan Take a webView and place it in the viewController where you are picking the document and make reference for the webView by creating a IBOutlet and after getting the url in the `didPickDocumentAt url: URL` delegate method, Now all you have to do is `webViewReference.loadRequest(URLRequest(url: !))` – Venkatesh Chejarla Aug 31 '18 at 05:18
  • 1
    don't forget to "import MobileCoreServices" otherwise String(kUTTypePDF) will give error – Muhammad Nayab Jan 03 '19 at 06:35
  • 1
    @Alexandros Update: I failed to make `documentPicker` as mentioned It looks like that's because https://developer.apple.com/documentation/uikit/uidocumentpickerdelegate/1618680-documentpicker was deprecated in favor of https://developer.apple.com/documentation/uikit/uidocumentpickerdelegate/2902364-documentpicker – Mayas May 25 '19 at 08:00
  • how can i use iit to use: .ovpn file – Blue_Alien Jul 24 '20 at 10:53
  • @Alexandros, in my app I permit my users to select ```kUTTypeArchive``` only, but when I tested the app, I was able to pick ```.docx``` as well. Is it possible to check the file type? Please, see: https://stackoverflow.com/questions/63521000/select-only-zip-file-in-uidocumentpickerviewcontroller – Lilya Aug 21 '20 at 10:34
  • Thanks for keeping your answer up to date! – Shalugin Feb 27 '21 at 10:13
39

You can use UIDocumentPickerViewController to get the files from the Files apps or iCloud Drive.

  1. Activate the Key-value storage and iCloud Documents from iCloud capability:

enter image description here

  1. Import the following framework on the view controller you want to open the document picker:

     import MobileCoreServices
    
     // For iOS 14+
     import UniformTypeIdentifiers
    
  2. Implement the following method from UIDocumentPickerDelegate:

     func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
          // you get from the urls parameter the urls from the files selected
     }
    
  3. Create an UIDocumentPickerViewController to display the File picker or iCloud Drive:

    // Use this code if your are developing prior iOS 14
    let types: [String] = [kUTTypePDF as String]
    let documentPicker = UIDocumentPickerViewController(documentTypes: types, in: .import)
    documentPicker.delegate = self
    documentPicker.modalPresentationStyle = .formSheet
    self.present(documentPicker, animated: true, completion: nil)
    
    // For iOS 14+
    let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: [UTType.item], asCopy: false)
    documentPicker.delegate = self
    documentPicker.modalPresentationStyle = .formSheet
    
    self.present(documentPicker, animated: true, completion: nil)
    

If you want the user can import more types of files to your app, you have to add more UTTypes to the types NSArray. To see all the types available, you can check the UTType Docs

When the document picker opens on iOS 11 and you try to select a file inside the Google Drive, it is possible the file is disable due a bug: http://www.openradar.me/24187873

pableiros
  • 14,932
  • 12
  • 99
  • 105
23

The UIDocumentMenuViewController is deprecated since iOS11. I also found it buggy when presented from a modal view controller. Here's a direct way of using the picker:

import MobileCoreServices

private func attachDocument() {
    let types = [kUTTypePDF, kUTTypeText, kUTTypeRTF, kUTTypeSpreadsheet]
    let importMenu = UIDocumentPickerViewController(documentTypes: types as [String], in: .import)

    if #available(iOS 11.0, *) {
        importMenu.allowsMultipleSelection = true
    }

    importMenu.delegate = self
    importMenu.modalPresentationStyle = .formSheet

    present(importMenu, animated: true)
}

extension AViewController: UIDocumentPickerDelegate, UINavigationControllerDelegate {
    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
        viewModel.attachDocuments(at: urls)
    }

     func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
        controller.dismiss(animated: true, completion: nil)
    }
}

As usual don't forget to add iCloud support: iCloud support

SoftDesigner
  • 5,640
  • 3
  • 58
  • 47
8

UIDocumentPickerViewController(documentTypes: [String], in: UIDocumentPickerMode) was deprecated in iOS 14.0

It is now UIDocumentPickerViewController(forOpeningContentTypes: [UTType])

ContentTypes being and array any or the combination of the following:

UTType.image, UTType.text, UTType.plainText, UTType.utf8PlainText,    UTType.utf16ExternalPlainText, UTType.utf16PlainText,    UTType.delimitedText, UTType.commaSeparatedText,    UTType.tabSeparatedText, UTType.utf8TabSeparatedText, UTType.rtf,    UTType.pdf, UTType.webArchive, UTType.image, UTType.jpeg,    UTType.tiff, UTType.gif, UTType.png, UTType.bmp, UTType.ico,    UTType.rawImage, UTType.svg, UTType.livePhoto, UTType.movie,    UTType.video, UTType.audio, UTType.quickTimeMovie, UTType.mpeg,    UTType.mpeg2Video, UTType.mpeg2TransportStream, UTType.mp3,    UTType.mpeg4Movie, UTType.mpeg4Audio, UTType.avi, UTType.aiff,    UTType.wav, UTType.midi, UTType.archive, UTType.gzip, UTType.bz2,    UTType.zip, UTType.appleArchive, UTType.spreadsheet, UTType.epub

This is how it works for me:

let supportedTypes = [myArrayFromAnyOfTheAbove]

func openDocument() {
    let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: supportedTypes, asCopy: true)
    documentPicker.delegate = self
    documentPicker.allowsMultipleSelection = false
    documentPicker.shouldShowFileExtensions = true
    present(documentPicker, animated: true, completion: nil)
}
Dilan
  • 2,610
  • 7
  • 23
  • 33
GIJoeCodes
  • 1,680
  • 1
  • 25
  • 28
4
let docsTypes = ["public.text",
                          "com.apple.iwork.pages.pages",
                          "public.data",
                          "kUTTypeItem",
                          "kUTTypeContent",
                          "kUTTypeCompositeContent",
                          "kUTTypeData",
                          "public.database",
                          "public.calendar-event",
                          "public.message",
                          "public.presentation",
                          "public.contact",
                          "public.archive",
                          "public.disk-image",
                          "public.plain-text",
                          "public.utf8-plain-text",
                          "public.utf16-external-plain-​text",
                          "public.utf16-plain-text",
                          "com.apple.traditional-mac-​plain-text",
                          "public.rtf",
                          "com.apple.ink.inktext",
                          "public.html",
                          "public.xml",
                          "public.source-code",
                          "public.c-source",
                          "public.objective-c-source",
                          "public.c-plus-plus-source",
                          "public.objective-c-plus-​plus-source",
                          "public.c-header",
                          "public.c-plus-plus-header",
                          "com.sun.java-source",
                          "public.script",
                          "public.assembly-source",
                          "com.apple.rez-source",
                          "public.mig-source",
                          "com.apple.symbol-export",
                          "com.netscape.javascript-​source",
                          "public.shell-script",
                          "public.csh-script",
                          "public.perl-script",
                          "public.python-script",
                          "public.ruby-script",
                          "public.php-script",
                          "com.sun.java-web-start",
                          "com.apple.applescript.text",
                          "com.apple.applescript.​script",
                          "public.object-code",
                          "com.apple.mach-o-binary",
                          "com.apple.pef-binary",
                          "com.microsoft.windows-​executable",
                          "com.microsoft.windows-​dynamic-link-library",
                          "com.sun.java-class",
                          "com.sun.java-archive",
                          "com.apple.quartz-​composer-composition",
                          "org.gnu.gnu-tar-archive",
                          "public.tar-archive",
                          "org.gnu.gnu-zip-archive",
                          "org.gnu.gnu-zip-tar-archive",
                          "com.apple.binhex-archive",
                          "com.apple.macbinary-​archive",
                          "public.url",
                          "public.file-url",
                          "public.url-name",
                          "public.vcard",
                          "public.image",
                          "public.fax",
                          "public.jpeg",
                          "public.jpeg-2000",
                          "public.tiff",
                          "public.camera-raw-image",
                          "com.apple.pict",
                          "com.apple.macpaint-image",
                          "public.png",
                          "public.xbitmap-image",
                          "com.apple.quicktime-image",
                          "com.apple.icns",
                          "com.apple.txn.text-​multimedia-data",
                          "public.audiovisual-​content",
                          "public.movie",
                          "public.video",
                          "com.apple.quicktime-movie",
                          "public.avi",
                          "public.mpeg",
                          "public.mpeg-4",
                          "public.3gpp",
                          "public.3gpp2",
                          "public.audio",
                          "public.mp3",
                          "public.mpeg-4-audio",
                          "com.apple.protected-​mpeg-4-audio",
                          "public.ulaw-audio",
                          "public.aifc-audio",
                          "public.aiff-audio",
                          "com.apple.coreaudio-​format",
                          "public.directory",
                          "public.folder",
                          "public.volume",
                          "com.apple.package",
                          "com.apple.bundle",
                          "public.executable",
                          "com.apple.application",
                          "com.apple.application-​bundle",
                          "com.apple.application-file",
                          "com.apple.deprecated-​application-file",
                          "com.apple.plugin",
                          "com.apple.metadata-​importer",
                          "com.apple.dashboard-​widget",
                          "public.cpio-archive",
                          "com.pkware.zip-archive",
                          "com.apple.webarchive",
                          "com.apple.framework",
                          "com.apple.rtfd",
                          "com.apple.flat-rtfd",
                          "com.apple.resolvable",
                          "public.symlink",
                          "com.apple.mount-point",
                          "com.apple.alias-record",
                          "com.apple.alias-file",
                          "public.font",
                          "public.truetype-font",
                          "com.adobe.postscript-font",
                          "com.apple.truetype-​datafork-suitcase-font",
                          "public.opentype-font",
                          "public.truetype-ttf-font",
                          "public.truetype-collection-​font",
                          "com.apple.font-suitcase",
                          "com.adobe.postscript-lwfn​-font",
                          "com.adobe.postscript-pfb-​font",
                          "com.adobe.postscript.pfa-​font",
                          "com.apple.colorsync-profile",
                          "public.filename-extension",
                          "public.mime-type",
                          "com.apple.ostype",
                          "com.apple.nspboard-type",
                          "com.adobe.pdf",
                          "com.adobe.postscript",
                          "com.adobe.encapsulated-​postscript",
                          "com.adobe.photoshop-​image",
                          "com.adobe.illustrator.ai-​image",
                          "com.compuserve.gif",
                          "com.microsoft.bmp",
                          "com.microsoft.ico",
                          "com.microsoft.word.doc",
                          "com.microsoft.excel.xls",
                          "com.microsoft.powerpoint.​ppt",
                          "com.microsoft.waveform-​audio",
                          "com.microsoft.advanced-​systems-format",
                          "com.microsoft.windows-​media-wm",
                          "com.microsoft.windows-​media-wmv",
                          "com.microsoft.windows-​media-wmp",
                          "com.microsoft.windows-​media-wma",
                          "com.microsoft.advanced-​stream-redirector",
                          "com.microsoft.windows-​media-wmx",
                          "com.microsoft.windows-​media-wvx",
                          "com.microsoft.windows-​media-wax",
                          "com.apple.keynote.key",
                          "com.apple.keynote.kth",
                          "com.truevision.tga-image",
                          "com.sgi.sgi-image",
                          "com.ilm.openexr-image",
                          "com.kodak.flashpix.image",
                          "com.j2.jfx-fax",
                          "com.js.efx-fax",
                          "com.digidesign.sd2-audio",
                          "com.real.realmedia",
                          "com.real.realaudio",
                          "com.real.smil",
                          "com.allume.stuffit-archive",
                          "org.openxmlformats.wordprocessingml.document",
                          "com.microsoft.powerpoint.​ppt",
                          "org.openxmlformats.presentationml.presentation",
                          "com.microsoft.excel.xls",
                          "org.openxmlformats.spreadsheetml.sheet",
                         
    
  ]
let documentPicker = UIDocumentPickerViewController(documentTypes: Utils.docsTypes, in: .import)
    documentPicker.delegate = self
    documentPicker.allowsMultipleSelection = true
    present(documentPicker, animated: true, completion: nil)
2

Something I struggled with was how to specify some specific formats for the PickerView, such as .pptx & .xlsx files. Here's some code to create a PickerView with some commonly required types...

let types: [String] = [
    kUTTypeJPEG as String,
    kUTTypePNG as String,
    "com.microsoft.word.doc",
    "org.openxmlformats.wordprocessingml.document",
    kUTTypeRTF as String,
    "com.microsoft.powerpoint.​ppt",
    "org.openxmlformats.presentationml.presentation",
    kUTTypePlainText as String,
    "com.microsoft.excel.xls",
    "org.openxmlformats.spreadsheetml.sheet",
    kUTTypePDF as String,
    kUTTypeMP3 as String
]
let documentPicker = UIDocumentPickerViewController(documentTypes: types, in: .import)
documentPicker.delegate = self
documentPicker.modalPresentationStyle = .formSheet
self.present(documentPicker, animated: true, completion: nil)

There are two places that I found useful in putting together this list:

https://developer.apple.com/library/archive/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html

https://escapetech.eu/manuals/qdrop/uti.html

Hope that helps somebody!

Ben
  • 4,707
  • 5
  • 34
  • 55
2

Here's a SwiftUI version of UIDocumentPickerViewController.

Credit goes to this blog post: https://capps.tech/blog/read-files-with-documentpicker-in-swiftui

I'm just posting the code here in case the blog post disappears so it's preserved.

(I adjusted the code slightly to copy the selected certificate file and write the Data to the Library folder; in the blog, it copies the contents of a text file.)

import SwiftUI
    
struct ContentView: View {
@State var fileContent:Data = Data()
@State var showDocumentPicker = false

var body: some View {
        
    Button() {
        showDocumentPicker = true
    } label: {
        Text("click me to show file browser")
    }
    .sheet(isPresented: self.$showDocumentPicker) {
        DocumentPicker(fileContent: $fileContent)
    }
}
}

struct DocumentPicker: UIViewControllerRepresentable {

    @Binding var fileContent: Data
    
    func makeCoordinator() -> DocumentPickerCoordinator {
        return DocumentPickerCoordinator(fileContent: $fileContent)
    }
    
    func makeUIViewController(context:
        UIViewControllerRepresentableContext<DocumentPicker>) ->
    UIDocumentPickerViewController {
        //The file types like ".pkcs12" are listed here:
        //https://developer.apple.com/documentation/uniformtypeidentifiers/system_declared_uniform_type_identifiers?changes=latest_minor
        let controller: UIDocumentPickerViewController = UIDocumentPickerViewController(forOpeningContentTypes: [.pkcs12], asCopy: true)
        controller.delegate = context.coordinator
    return controller
    }
    
    func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<DocumentPicker>) {
        print("update")
    }
} //struct

class DocumentPickerCoordinator: NSObject, UIDocumentPickerDelegate, UINavigationControllerDelegate {

    @Binding var fileContent: Data
    
    init(fileContent: Binding<Data>) {
        _fileContent = fileContent
    }

    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
        let fileURL = urls[0]
        
        let certData = try! Data(contentsOf: fileURL)
        
        if let documentsPathURL = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first {
            let certURL = documentsPathURL.appendingPathComponent("certFile.pfx")
            
            try? certData.write(to: certURL)
        }

    }
}
James Toomey
  • 5,635
  • 3
  • 37
  • 41
1

this will help you to implement download/upload functionality

UIDocumentMenuViewController *importMenu = [[UIDocumentMenuViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeImport | UIDocumentPickerModeExportToService];

For more read Apple Documentation

Rajesh
  • 10,318
  • 16
  • 44
  • 64
1

iCloud access all type of files #

func openiCloudDocuments(){
    let importMenu = UIDocumentPickerViewController(documentTypes: [String("public.data")], in: .import)
    importMenu.delegate = self
    importMenu.modalPresentationStyle = .formSheet
    self.present(importMenu, animated: true, completion: nil)
}
-1

You could implement what you describe using NSURLSession.

You will have to limit the target directory you show to your app's documents directory. Apps do not have full access to the file system.

Duncan C
  • 128,072
  • 22
  • 173
  • 272