How to copy selected file to app's directory

£可爱£侵袭症+ 提交于 2021-01-29 08:08:37

问题


I'm new to FileManager in iOS. I'm trying to create a simple app: there's a list of files, and when the user press a cell the the file opens. The user can also add new files.

This is what I currently have:

class TableViewController: UITableViewController, UIDocumentPickerDelegate {

var urls: [URL] = []

override func viewDidLoad() {
    super.viewDidLoad()

    // Uncomment the following line to preserve selection between presentations
    // self.clearsSelectionOnViewWillAppear = false

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem
}

@IBAction func newButtonPressed(_ sender: Any) {
    let types = UTType(tag: "pdf", tagClass: .filenameExtension, conformingTo: nil)!
    let controller = UIDocumentPickerViewController(forOpeningContentTypes: [types])
    controller.delegate = self
    self.present(controller, animated: true, completion: nil)
}

func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
    for url in urls {
        self.storeAndShare(withURL: url)
    }
}

// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
    // #warning Incomplete implementation, return the number of sections
    return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of rows
    return urls.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = UITableViewCell(style: .default, reuseIdentifier: nil)

    cell.textLabel?.text = urls[indexPath.row].localizedName

    return cell
}

lazy var documentInteractionController: UIDocumentInteractionController = {
    let vc = UIDocumentInteractionController()
    vc.delegate = self
    return vc
}()

func share(url: URL) {
    documentInteractionController.url = url
    documentInteractionController.uti = url.typeIdentifier ?? "public.data, public.content"
    documentInteractionController.name = url.localizedName ?? url.lastPathComponent
    documentInteractionController.presentPreview(animated: true)
}

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    share(url: self.urls[indexPath.row])
}

func storeAndShare(withURL url: URL) {
    /// START YOUR ACTIVITY INDICATOR HERE
    URLSession.shared.dataTask(with: url) { data, response, error in
        guard let data = data, error == nil else { return }
        let direcotryURL = URL(string: FileManager.default.currentDirectoryPath)!
        let newURL = direcotryURL.appendingPathComponent(response?.suggestedFilename ?? "fileName.pdf")
        
        do {
            try data.write(to: newURL)
        } catch let err {
            print("ERROR: \(err).")

            // Error Domain=NSCocoaErrorDomain Code=518 "The file couldn’t be saved because the specified URL type isn’t supported." UserInfo={NSURL=/doc.pdf}

            return
        }
        
        DispatchQueue.main.async {
            /// STOP YOUR ACTIVITY INDICATOR HERE
            self.urls.append(newURL)
            self.share(url: newURL)
            self.tableView.reloadData()
        }
        }.resume()
}

}

extension TableViewController: UIDocumentInteractionControllerDelegate {
/// If presenting atop a navigation stack, provide the navigation controller in order to animate in a manner consistent with the rest of the platform
func documentInteractionControllerViewControllerForPreview(_ controller:  UIDocumentInteractionController) -> UIViewController {
    guard let navVC = self.navigationController else {
        return self
    }
    return navVC
}
}
extension URL {
var typeIdentifier: String? {
    return (try? resourceValues(forKeys: [.typeIdentifierKey]))?.typeIdentifier
}
var localizedName: String? {
    return (try? resourceValues(forKeys: [.localizedNameKey]))?.localizedName
}
}

I have problems with the storeAndShare() function. I'm trying to copy the selected file to the app's directory, and then add the new URL to the urls list (self.urls). But it doesn't work, and gives me an error in data.write:

Error Domain=NSCocoaErrorDomain Code=518 "The file couldn’t be saved because the specified URL type isn’t supported." UserInfo={NSURL=/doc.pdf}

Why is this happening? and how can I achieve what I need? thanks in advance


回答1:


The most significant mistake is currentDirectoryPath. You must not use it to get the path to the Documents directory. You have to call url(for:in:appropriateFor:create: or urls(for:in:) of FileManager

And – if we are talking about local files – URLSession is the wrong way, too.

func storeAndShare(with url: URL) {

    let directoryURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
    let newURL = directoryURL.appendingPathComponent(url.lastPathComponent)
    
    do {
        try FileManager.default.copyItem(at: url, to: newURL)
        DispatchQueue.main.async {
            self.urls.append(newURL)
            self.share(url: newURL)
            self.tableView.reloadData()
        }
    } catch {
        print("ERROR: \(error).")
    }
}



回答2:


"/doc.pdf"

Is not writable for you.

Get the documents directory:

let fileManager = FileManager.default
let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]

And save your file there (instead of using currentDirectoryPath).



来源:https://stackoverflow.com/questions/65407772/how-to-copy-selected-file-to-apps-directory

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!