How to access files in iCloud Drive from within my iOS app?

后端 未结 5 515
[愿得一人]
[愿得一人] 2020-12-03 02:06

Is there a way to choose file from iCloud Drive similar way to UIImagePickerController()?

相关标签:
5条回答
  • 2020-12-03 02:20

    The document picker calls the delegate’s documentPicker:didPickDocumentAtURL: method when the user selects a destination outside your app’s sandbox. The system saves a copy of your document to the specified destination. The document picker provides the copy’s URL to indicate success; however, your app does not have access to the file referred to by this URL. Link

    This code work for me:

    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
            let url = urls[0]
            let isSecuredURL = url.startAccessingSecurityScopedResource() == true
            let coordinator = NSFileCoordinator()
            var error: NSError? = nil
            coordinator.coordinate(readingItemAt: url, options: [], error: &error) { (url) -> Void in
                _ = urls.compactMap { (url: URL) -> URL? in
                    // Create file URL to temporary folder
                    var tempURL = URL(fileURLWithPath: NSTemporaryDirectory())
                    // Apend filename (name+extension) to URL
                    tempURL.appendPathComponent(url.lastPathComponent)
                    do {
                        // If file with same name exists remove it (replace file with new one)
                        if FileManager.default.fileExists(atPath: tempURL.path) {
                            try FileManager.default.removeItem(atPath: tempURL.path)
                        }
                        // Move file from app_id-Inbox to tmp/filename
                        try FileManager.default.moveItem(atPath: url.path, toPath: tempURL.path)
    
    
                        YourFunction(tempURL)
                        return tempURL
                    } catch {
                        print(error.localizedDescription)
                        return nil
                    }
                }
            }
            if (isSecuredURL) {
                url.stopAccessingSecurityScopedResource()
            }
    
            navigationController?.dismiss(animated: true, completion: nil)
        }
    
    0 讨论(0)
  • 2020-12-03 02:23

    iCloudUrl.startAccessingSecurityScopedResource() // is returning true for me at this point,

    However the following code gave the error:

    try FileManager.default.createDirectory(atPath: iCloudUrl, withIntermediateDirectories: true, attributes: nil)

    "You can’t save the file “xyz” because the volume is read only."

    This actually works :
    try FileManager.default.createDirectory(at: iCloudUrl, withIntermediateDirectories: true, attributes: nil)

    Which makes sense because the URL probably is carrying around it’s security access, but this little oversight stumped me for half a day…

    0 讨论(0)
  • 2020-12-03 02:24

    You can present controller the following way:

    import MobileCoreServices
    
    let documentPickerController = UIDocumentPickerViewController(documentTypes: [String(kUTTypePDF), String(kUTTypeImage), String(kUTTypeMovie), String(kUTTypeVideo), String(kUTTypePlainText), String(kUTTypeMP3)], inMode: .Import)
    documentPickerController.delegate = self
    presentViewController(documentPickerController, animated: true, completion: nil)
    
    

    In your delegate implement the method:

    func documentPicker(controller: UIDocumentPickerViewController, didPickDocumentAtURL url: NSURL)
    

    Note that you don't need to set up iCloud Entitlement to use UIDocumentPickerViewController. Apple provides sample code that demonstrates how to use this controller here

    0 讨论(0)
  • 2020-12-03 02:38

    Swift 5, iOS 13

    Jhonattan's and Ashu's answers are definitely on the right track for the core functionality, there are a number of issues with multiple-document-selection, error outcomes and deprecated document picker API.

    The code below shows a modern start-to-finish version of a common use case: pick an external iCloud document to import into app and do something with it.

    Note that you have to have your app's Capabilities set up to use iCloud documents and have a ubiquity container set up in your app's .plist... See for example: Swift write/save/move a document file to iCloud drive

    class ViewController: UIViewController {
        
        @IBAction func askForDocument(_ sender: Any) {
            
            if FileManager.default.url(forUbiquityContainerIdentifier: nil) != nil {
    
                let iOSPickerUI = UIDocumentPickerViewController(documentTypes: ["public.text"], in: .import)
                iOSPickerUI.delegate = self
                iOSPickerUI.modalPresentationStyle = .formSheet
                
                if let popoverPresentationController = iOSPickerUI.popoverPresentationController {
                    popoverPresentationController.sourceView = sender as? UIView
                }
                self.present(iOSPickerUI, animated: true, completion: nil)
            }
        }
    
        func processImportedFileAt(fileURL: URL) {
            // ...
        }
    }
    
    extension ViewController: UIDocumentPickerDelegate, UINavigationControllerDelegate {
        
        func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
            dismiss(animated: true, completion: nil)
        }
        
        func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
            if controller.allowsMultipleSelection {
                print("WARNING: controller allows multiple file selection, but coordinate-read code here assumes only one file chosen")
                // If this is intentional, you need to modify the code below to do coordinator.coordinate
                // on MULTIPLE items, not just the first one
                if urls.count > 0 { print("Ignoring all but the first chosen file") }
            }
            
            let firstFileURL = urls[0]
            let isSecuredURL = (firstFileURL.startAccessingSecurityScopedResource() == true)
            
            print("UIDocumentPickerViewController gave url = \(firstFileURL)")
    
            // Status monitoring for the coordinate block's outcome
            var blockSuccess = false
            var outputFileURL: URL? = nil
    
            // Execute (synchronously, inline) a block of code that will copy the chosen file
            // using iOS-coordinated read to cooperate on access to a file we do not own:
            let coordinator = NSFileCoordinator()
            var error: NSError? = nil
            coordinator.coordinate(readingItemAt: firstFileURL, options: [], error: &error) { (externalFileURL) -> Void in
                    
                // WARNING: use 'externalFileURL in this block, NOT 'firstFileURL' even though they are usually the same.
                // They can be different depending on coordinator .options [] specified!
            
                // Create file URL to temp copy of file we will create:
                var tempURL = URL(fileURLWithPath: NSTemporaryDirectory())
                tempURL.appendPathComponent(externalFileURL.lastPathComponent)
                print("Will attempt to copy file to tempURL = \(tempURL)")
                
                // Attempt copy
                do {
                    // If file with same name exists remove it (replace file with new one)
                    if FileManager.default.fileExists(atPath: tempURL.path) {
                        print("Deleting existing file at: \(tempURL.path) ")
                        try FileManager.default.removeItem(atPath: tempURL.path)
                    }
                    
                    // Move file from app_id-Inbox to tmp/filename
                    print("Attempting move file to: \(tempURL.path) ")
                    try FileManager.default.moveItem(atPath: externalFileURL.path, toPath: tempURL.path)
                    
                    blockSuccess = true
                    outputFileURL = tempURL
                }
                catch {
                    print("File operation error: " + error.localizedDescription)
                    blockSuccess = false
                }
                
            }
            navigationController?.dismiss(animated: true, completion: nil)
            
            if error != nil {
                print("NSFileCoordinator() generated error while preparing, and block was never executed")
                return
            }
            if !blockSuccess {
                print("Block executed but an error was encountered while performing file operations")
                return
            }
            
            print("Output URL : \(String(describing: outputFileURL))")
            
            if (isSecuredURL) {
                firstFileURL.stopAccessingSecurityScopedResource()
            }
            
            if let out = outputFileURL {
                processImportedFileAt(fileURL: out)
            }
        }
    
    }
    
    0 讨论(0)
  • 2020-12-03 02:39

    Swift 4.X

    You need to enable iCloud entitlements in XCode Capabilities. Also you have to turn on iCloud in you app bundle in developer account of Apple. Once you do this, you are able to present document picker controller by following way:

    Use UIDocumentPickerDelegate methods

    extension YourViewController : UIDocumentMenuDelegate, UIDocumentPickerDelegate,UINavigationControllerDelegate {
    
        func documentMenu(_ documentMenu: UIDocumentMenuViewController, didPickDocumentPicker documentPicker: UIDocumentPickerViewController) {
            documentPicker.delegate = self
            self.present(documentPicker, animated: true, completion: nil)
        }
    
        func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) {
            print("url = \(url)")
        }
    
        func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
            dismiss(animated: true, completion: nil)    
        }
    }
    

    Add below code for Button Action

    @IBAction func didPressAttachment(_ sender: UIButton) {
    
            let importMenu = UIDocumentMenuViewController(documentTypes: [String(kUTTypePDF)], in: .import)
            importMenu.delegate = self
            importMenu.modalPresentationStyle = .formSheet
    
            if let popoverPresentationController = importMenu.popoverPresentationController {
                popoverPresentationController.sourceView = sender
                // popoverPresentationController.sourceRect = sender.bounds
            }
             self.present(importMenu, animated: true, completion: nil)
    
        }
    

    This is working fine for me. Hope it helps you too.

    Happy coding :)

    0 讨论(0)
提交回复
热议问题