Reputation:
I want to pick a file of any type (.pdf, .docs, .xlsx, .jpeg, .txt, .rtf, etc.) 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?
Upvotes: 76
Views: 114937
Reputation: 2203
Slight update for iOS 17:
Add only ...
import UIKit // nothing else
class Job: UIViewController, UIDocumentPickerDelegate {
// delete "UIDocumentMenuDelegate"
And then ...
@IBAction func tapSendFile() {
let pdfs = UTType.types(tag: "pdf",
tagClass: UTTagClass.filenameExtension, conformingTo: nil)
let txts = UTType.types(tag: "txt",
tagClass: UTTagClass.filenameExtension, conformingTo: nil)
// as many of those as you wish, then:
let doxy = UIDocumentPickerViewController(
forOpeningContentTypes: pdfs + txts)
doxy.delegate = self
self.present(doxy, animated: true, completion: nil)
}
Latest syntax:
func documentPicker(_ controller: UIDocumentPickerViewController,
didPickDocumentsAt urls: [URL]) {
print("IT WORKED")
}
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.
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
.
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 ..
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.
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") })
Enjoy it.
Upvotes: 121
Reputation: 6092
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)
}
}
}
Upvotes: 2
Reputation: 16080
You can use UIDocumentPickerViewController
to get the files from the Files apps or iCloud Drive.
Key-value storage
and iCloud Documents
from iCloud
capability:Import the following framework on the view controller you want to open the document picker:
import MobileCoreServices
// For iOS 14+
import UniformTypeIdentifiers
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
}
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 UTType
s 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
Upvotes: 40
Reputation: 1850
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)
}
Upvotes: 9
Reputation: 109
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)
Upvotes: 4
Reputation: 4867
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://escapetech.eu/manuals/qdrop/uti.html
Hope that helps somebody!
Upvotes: 3
Reputation: 11
func openiCloudDocuments(){
let importMenu = UIDocumentPickerViewController(documentTypes: [String("public.data")], in: .import)
importMenu.delegate = self
importMenu.modalPresentationStyle = .formSheet
self.present(importMenu, animated: true, completion: nil)
}
Upvotes: 1
Reputation: 5853
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:
Upvotes: 24
Reputation: 10434
this will help you to implement download/upload functionality
UIDocumentMenuViewController *importMenu = [[UIDocumentMenuViewController alloc] initWithDocumentTypes:@[@"public.item"] inMode:UIDocumentPickerModeImport | UIDocumentPickerModeExportToService];
For more read Apple Documentation
Upvotes: 1
Reputation: 131511
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.
Upvotes: -1