diff --git a/README.md b/README.md
index 869ace5..953856e 100644
--- a/README.md
+++ b/README.md
@@ -254,11 +254,12 @@ Only available on Android and iOS.
#### PickFilesOptions
-| Prop | Type | Description | Default |
-| -------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
-| **`types`** | string[]
| List of accepted file types. Look at [IANA Media Types](https://www.iana.org/assignments/media-types/media-types.xhtml) for a complete list of standard media types. This option cannot be used with `multiple: true` on Android. | |
-| **`multiple`** | boolean
| Whether multiple files may be selected. | false
|
-| **`readData`** | boolean
| Whether to read the file data. | false
|
+| Prop | Type | Description | Default |
+| ---------------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
+| **`types`** | string[]
| List of accepted file types. Look at [IANA Media Types](https://www.iana.org/assignments/media-types/media-types.xhtml) for a complete list of standard media types. This option cannot be used with `multiple: true` on Android. | |
+| **`customExtensions`** | string[]
| iOS only. List of custom file name extensions (without leading dot '.'). Necessary in iOS since custom mimetypes are not supported. Example: `['cs2']` | |
+| **`multiple`** | boolean
| Whether multiple files may be selected. | false
|
+| **`readData`** | boolean
| Whether to read the file data. | false
|
#### PickMediaOptions
diff --git a/ios/Plugin/FilePicker.swift b/ios/Plugin/FilePicker.swift
index 3611631..effb768 100644
--- a/ios/Plugin/FilePicker.swift
+++ b/ios/Plugin/FilePicker.swift
@@ -4,6 +4,7 @@ import Photos
import Capacitor
import UIKit
import MobileCoreServices
+import UniformTypeIdentifiers
@objc public class FilePicker: NSObject {
private var plugin: FilePickerPlugin?
@@ -96,6 +97,17 @@ import MobileCoreServices
}
}
}
+
+ @available(iOS 14.0, *)
+ public func openDocumentPickerWithFileExtensions(multiple: Bool, documentTypes: [UTType]) {
+ DispatchQueue.main.async {
+ let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: documentTypes)
+ documentPicker.delegate = self
+ documentPicker.allowsMultipleSelection = multiple
+ documentPicker.modalPresentationStyle = .fullScreen
+ self.plugin?.bridge?.viewController?.present(documentPicker, animated: true, completion: nil)
+ }
+ }
public func getPathFromUrl(_ url: URL) -> String {
return url.absoluteString
diff --git a/ios/Plugin/FilePickerPlugin.swift b/ios/Plugin/FilePickerPlugin.swift
index b5f3320..0f5d170 100644
--- a/ios/Plugin/FilePickerPlugin.swift
+++ b/ios/Plugin/FilePickerPlugin.swift
@@ -2,6 +2,7 @@ import Foundation
import Capacitor
import UIKit
import MobileCoreServices
+import UniformTypeIdentifiers
/**
* Please read the Capacitor iOS Plugin Development Guide
@@ -53,10 +54,20 @@ public class FilePickerPlugin: CAPPlugin {
let multiple = call.getBool("multiple", false)
let types = call.getArray("types", String.self) ?? []
- let parsedTypes = parseTypesOption(types)
- let documentTypes = parsedTypes.isEmpty ? ["public.data"] : parsedTypes
-
- implementation?.openDocumentPicker(multiple: multiple, documentTypes: documentTypes)
+ let fileExtensions = call.getArray("customExtensions", String.self) ?? []
+ if #available(iOS 14.0, *) {
+ let parsedTypes = parseTypesOptionsToUTTypes(types)
+ let parsedExtensions = parseCustomExtensions(fileExtensions)
+ let concatenatedTypes = parsedTypes + parsedExtensions
+ let documentTypes = concatenatedTypes.isEmpty ? [.data] : concatenatedTypes
+ implementation?.openDocumentPickerWithFileExtensions(multiple: multiple, documentTypes: documentTypes)
+ } else {
+ // Fallback on earlier versions
+ let parsedTypes = parseTypesOption(types)
+ let documentTypes = parsedTypes.isEmpty ? ["public.data"] : parsedTypes
+
+ implementation?.openDocumentPicker(multiple: multiple, documentTypes: documentTypes)
+ }
}
@objc func pickImages(_ call: CAPPluginCall) {
@@ -94,6 +105,30 @@ public class FilePickerPlugin: CAPPlugin {
return parsedTypes
}
+ @available(iOS 14.0, *)
+ @objc func parseTypesOptionsToUTTypes(_ types: [String]) -> [UTType] {
+ var parsedTypes: [UTType] = []
+ for (_, type) in types.enumerated() {
+ guard let utType: UTType = UTType(mimeType: type) else {
+ continue
+ }
+ parsedTypes.append(utType)
+ }
+ return parsedTypes
+ }
+
+ @available(iOS 14.0, *)
+ @objc func parseCustomExtensions(_ extensions: [String]) -> [UTType] {
+ var parsedExtensions: [UTType] = []
+ for (_, exten) in extensions.enumerated() {
+ guard let utType: UTType = UTType(filenameExtension: exten) else {
+ continue
+ }
+ parsedExtensions.append(utType)
+ }
+ return parsedExtensions
+ }
+
@objc func handleDocumentPickerResult(urls: [URL]?, error: String?) {
guard let savedCall = savedCall else {
return
@@ -107,9 +142,18 @@ public class FilePickerPlugin: CAPPlugin {
return
}
let readData = savedCall.getBool("readData", false)
+ for (url) in urls {
+ guard url.startAccessingSecurityScopedResource() else {
+ return
+ }
+ }
do {
var result = JSObject()
let filesResult = try urls.map {(url: URL) -> JSObject in
+ guard url.startAccessingSecurityScopedResource() else {
+ let error = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "File access denied"])
+ throw error
+ }
var file = JSObject()
if readData == true {
file["data"] = try implementation?.getDataFromUrl(url) ?? ""
@@ -130,6 +174,7 @@ public class FilePickerPlugin: CAPPlugin {
file["name"] = implementation?.getNameFromUrl(url) ?? ""
file["path"] = implementation?.getPathFromUrl(url) ?? ""
file["size"] = try implementation?.getSizeFromUrl(url) ?? -1
+ url.stopAccessingSecurityScopedResource()
return file
}
result["files"] = filesResult
diff --git a/src/definitions.ts b/src/definitions.ts
index b012481..acc7d66 100644
--- a/src/definitions.ts
+++ b/src/definitions.ts
@@ -81,6 +81,12 @@ export interface PickFilesOptions {
* @example ['image/png', 'application/pdf']
*/
types?: string[];
+ /**
+ * iOS only. List of custom file name extensions (without leading dot '.'). Necessary in iOS since custom mimetypes are not supported.
+ *
+ * Example: `['cs2']`
+ */
+ customExtensions?: string[];
/**
* Whether multiple files may be selected.
*