简体   繁体   English

如何从我的 iOS 应用程序中访问 iCloud Drive 中的文件?

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

有没有办法从 iCloud Drive 中选择文件,类似于UIImagePickerController()

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 .请注意,您无需设置 iCloud 授权即可使用UIDocumentPickerViewController Apple provides sample code that demonstrates how to use this controller here Apple在此处提供了演示如何使用此控制器的示例代码

The document picker calls the delegate's documentPicker:didPickDocumentAtURL: method when the user selects a destination outside your app's sandbox.当用户选择应用程序沙箱之外的目的地时,文档选择器会调用委托的 documentPicker:didPickDocumentAtURL: 方法。 The system saves a copy of your document to the specified destination.系统将您的文档副本保存到指定的目的地。 The document picker provides the copy's URL to indicate success;文档选择器提供副本的 URL 以指示成功; however, your app does not have access to the file referred to by this URL.但是,您的应用程序无权访问此 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)
    }

Swift 4.X斯威夫特 4.X

You need to enable iCloud entitlements in XCode Capabilities.您需要在 XCode 功能中启用iCloud权利。 Also you have to turn on iCloud in you app bundle in developer account of Apple.此外,您必须在 Apple 的开发者帐户中的应用程序包中打开iCloud Once you do this, you are able to present document picker controller by following way:完成此操作后,您可以通过以下方式呈现文档选择器控制器:

Use UIDocumentPickerDelegate methods使用UIDocumentPickerDelegate方法

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 :)快乐编码:)

This changed once again in iOS 14 !!这在iOS 14 中再次改变了!!

Working example for JSON: JSON 的工作示例:

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

Swift 5, iOS 13斯威夫特 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. Jhonattan 和 Ashu 的答案对于核心功能来说绝对是正确的,多文档选择、错误结果和不推荐使用的文档选择器 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 .下面的代码显示了一个常见用例的现代自始至终版本:选择一个外部 iCloud 文档以导入应用程序并对其进行处理

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请注意,您必须将应用程序的功能设置为使用 iCloud 文档,并在应用程序的 .plist 中设置一个无处不在的容器...参见示例: 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)
        }
    }

}

iCloudUrl.startAccessingSecurityScopedResource() // is returning true for me at this point, iCloudUrl.startAccessingSecurityScopedResource() // 此时为我返回 true,

However the following code gave the error:但是下面的代码给出了错误:

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

"You can't save the file “xyz” because the volume is read only." “您无法保存文件“xyz”,因为该卷是只读的。”

This actually works :这实际上有效:
try FileManager.default.createDirectory(at: iCloudUrl, withIntermediateDirectories: true, attributes: nil)尝试 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…这是有道理的,因为 URL 可能带有它的安全访问权限,但是这个小小的疏忽让我难住了半天……

For my swiftUI users: It's quite easy.对于我的 swiftUI 用户:这很容易。

struct HomeView: View {
    
    @State private var showActionSheet = false
    
    var body: some View {
        ButtonComponentView(title: "Press", handler: {
            showActionSheet = true
        })
         .fileImporter(isPresented: $showActionSheet, allowedContentTypes: [.data]) { (res) in
                print("!!!\(res)")
            }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM