22
33import Foundation
44import UniformTypeIdentifiers
5- import MobileCoreServices
65
7- @objc public class DocPicker : PickerWithMetadataImpl {
6+ @objc public class DocPicker : PickerBase {
7+ var pickerOptions : PickerOptions ?
8+ var openedUrls : Set < URL > = [ ]
89
9- var currentOptions : PickerOptions ? = nil
10+ @MainActor
11+ override func createDocumentPicker( from dictionary: NSDictionary ) -> UIDocumentPickerViewController {
12+ let options = PickerOptions ( dictionary: dictionary)
13+ self . pickerOptions = options
14+ return options. createDocumentPicker ( )
15+ }
1016
11- @objc public func present( options: PickerOptions , resolve: @escaping RNDPPromiseResolveBlock , reject: @escaping RNDPPromiseRejectBlock ) {
12- // TODO fix callsite param
13- if ( !promiseWrapper. trySetPromiseRejectingIncoming ( resolve, rejecter: reject, fromCallSite: " pick " ) ) {
14- return ;
15- }
16- currentOptions = options;
17- DispatchQueue . main. async {
18- let documentPicker = UIDocumentPickerViewController ( forOpeningContentTypes: options. allowedTypes, asCopy: options. modeAsCopy ( ) )
17+ public override func documentPicker( _ controller: UIDocumentPickerViewController , didPickDocumentsAt urls: [ URL ] ) {
18+ guard let promise = promiseWrapper. takeCallbacks ( ) else { return }
19+ let options = self . pickerOptions
1920
20- documentPicker. modalPresentationStyle = options. presentationStyle
21- documentPicker. allowsMultipleSelection = options. allowMultiSelection
22- documentPicker. modalTransitionStyle = options. transitionStyle
23- // documentPicker.directoryURL = options.initialDirectoryUrl
24- // documentPicker.shouldShowFileExtensions = options.shouldShowFileExtensions
21+ Task . detached ( priority: . userInitiated) {
22+ let documentsInfo = self . createDocumentMetadataWithOptions ( for: urls, options: options)
23+ . compactMap { $0. build ( ) }
24+ promise. resolve ( documentsInfo)
25+ }
26+ }
2527
26- self . presentInternal ( documentPicker: documentPicker)
28+ nonisolated private func createDocumentMetadataWithOptions( for urls: [ URL ] , options: PickerOptions ? ) -> [ DocumentMetadataBuilder ] {
29+ return urls. compactMap { url in
30+ do {
31+ return try self . getMetadataForWithOptions ( url: url, options: options)
32+ } catch {
33+ return DocumentMetadataBuilder ( forUri: url, error: error)
34+ }
2735 }
2836 }
2937
30- public func getMetadataFor ( url: URL ) throws -> DocumentMetadataBuilder {
31- return if ( currentOptions ? . isOpenMode ( ) == true ) {
32- try self . getOpenedDocumentInfo ( url: url, requestLongTermAccess: currentOptions ? . requestLongTermAccess ?? false )
38+ nonisolated private func getMetadataForWithOptions ( url: URL , options : PickerOptions ? ) throws -> DocumentMetadataBuilder {
39+ return if options ? . isOpenMode ( ) == true {
40+ try self . getOpenedDocumentInfo ( url: url, requestLongTermAccess: options ? . requestLongTermAccess ?? false )
3341 } else {
3442 try self . getAnyModeMetadata ( url: url)
3543 }
3644 }
3745
38- private func getAnyModeMetadata( url: URL ) throws -> DocumentMetadataBuilder {
46+ nonisolated private func getAnyModeMetadata( url: URL ) throws -> DocumentMetadataBuilder {
3947 let resourceValues = try url. resourceValues ( forKeys: [ . fileSizeKey, . nameKey, . isDirectoryKey, . contentTypeKey] )
4048
4149 return DocumentMetadataBuilder ( forUri: url, resourceValues: resourceValues)
@@ -45,18 +53,20 @@ import MobileCoreServices
4553 case sourceAccessError
4654 }
4755
48- func getOpenedDocumentInfo( url: URL , requestLongTermAccess: Bool ) throws -> DocumentMetadataBuilder {
56+ nonisolated private func getOpenedDocumentInfo( url: URL , requestLongTermAccess: Bool ) throws -> DocumentMetadataBuilder {
4957 guard url. startAccessingSecurityScopedResource ( ) else {
5058 throw KeepLocalCopyError . sourceAccessError
5159 }
5260
53- // url.stopAccessingSecurityScopedResource() must be called later
54- openedUrls. append ( url)
61+ // url.stopAccessingSecurityScopedResource() must be called later by user
62+ DispatchQueue . main. async { [ weak self] in
63+ self ? . openedUrls. insert ( url)
64+ }
5565
56- // Use file coordination for reading and writing any of the URL’ s content.
66+ // Use file coordination for reading and writing any of the URL' s content.
5767 var error : NSError ? = nil
5868 var success = false
59- var metadataBuilder : DocumentMetadataBuilder = DocumentMetadataBuilder ( forUri: url)
69+ var metadataBuilder = DocumentMetadataBuilder ( forUri: url)
6070
6171 NSFileCoordinator ( ) . coordinate ( readingItemAt: url, error: & error) { ( url) in
6272 do {
@@ -66,7 +76,7 @@ import MobileCoreServices
6676 metadataBuilder. setMetadataReadingError ( error)
6777 }
6878
69- if ( requestLongTermAccess == true ) {
79+ if requestLongTermAccess {
7080 do {
7181 let bookmarkData = try url. bookmarkData ( options: . minimalBookmark, includingResourceValuesForKeys: nil , relativeTo: nil )
7282 metadataBuilder. setBookmark ( bookmarkData)
@@ -75,10 +85,18 @@ import MobileCoreServices
7585 }
7686 }
7787 }
78- if let err = error, success == false {
88+ if let err = error, ! success {
7989 throw err
8090 }
8191 return metadataBuilder
8292 }
8393
94+ @objc public func stopAccessingOpenedUrls( _ urlStrings: [ String ] ) {
95+ let incomingUrls = Set ( urlStrings. compactMap { URL ( string: $0) } )
96+ for url in openedUrls. intersection ( incomingUrls) {
97+ url. stopAccessingSecurityScopedResource ( )
98+ openedUrls. remove ( url)
99+ }
100+ }
101+
84102}
0 commit comments