In this tutorial, we will see how to zip a file using FileManager in Swift. First, we will import a file using UIDocumentPicker. Second, we will zip that file using FileManager. Third and finally, we will share the zipped file using UIActivityViewController.

Importing a file using UIDocumentPicker

First, add UIDocumentPickerDelegate to your view controller, then override the viewDidAppear method, and add the code below. This will open the document picker when you run the app.

class ViewController: UIViewController, UIDocumentPickerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewDidAppear(_ animated: Bool) {
        let docsTypes = ["public.data"]
        let documentPicker = UIDocumentPickerViewController(documentTypes: docsTypes,
                                                            in: .import)
        documentPicker.delegate = self
        documentPicker.allowsMultipleSelection = false
        self.present(documentPicker, animated: true, completion: nil)
    }
}

Then, implement the didPickDocumentAt method of UIDocumentPicker, by adding the following code. This method will be called when the user has selected a file, and then call the zip method, which we implement next.

    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
        zip(sourceUrl: urls[0])
    }

Zipping a file using FileManager

Second, implement the zip method by adding the following code. This method will 1) get the documents directory of the app, 2) move the selected file to the documents directory, and 3) zip the documents directory and move the zip to a temporary URL.

    func zip(sourceUrl: URL) {
        let fm = FileManager.default
        var documentsUrl = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
        
        do {
            let fileUrl = documentsUrl.appendingPathComponent(sourceUrl.lastPathComponent)
            try? fm.removeItem(at: fileUrl)
            try fm.moveItem(at: sourceUrl, to: fileUrl)
            
            var archiveUrl: URL?
            var error: NSError?
            NSFileCoordinator().coordinate(readingItemAt: documentsUrl, options: [.forUploading], error: &error) { (zipUrl) in
                let tmpUrl = try! fm.url(for: .itemReplacementDirectory, in: .userDomainMask, appropriateFor: zipUrl, create: true
                ).appendingPathComponent("archive.zip")
                try! fm.moveItem(at: zipUrl, to: tmpUrl)
                archiveUrl = tmpUrl
            }
        } catch {
            print(error)
        }
    }
}

Sharing a zipped file using UIActivityViewController

Third and finally, add the UIActivityViewController code to the zip method so it looks like below. This will open the activity view so the user can share or save the zipped file locally.

    func zip(sourceUrl: URL) {
        let fm = FileManager.default
        var documentsUrl = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
        
        do {
            let fileUrl = documentsUrl.appendingPathComponent(sourceUrl.lastPathComponent)
            try? fm.removeItem(at: fileUrl)
            try fm.moveItem(at: sourceUrl, to: fileUrl)
            
            var archiveUrl: URL?
            var error: NSError?
            NSFileCoordinator().coordinate(readingItemAt: documentsUrl, options: [.forUploading], error: &error) { (zipUrl) in
                let tmpUrl = try! fm.url(for: .itemReplacementDirectory, in: .userDomainMask, appropriateFor: zipUrl, create: true
                ).appendingPathComponent("archive.zip")
                try! fm.moveItem(at: zipUrl, to: tmpUrl)
                archiveUrl = tmpUrl
            }

            if let archiveUrl = archiveUrl {
                let avc = UIActivityViewController(activityItems: [archiveUrl], applicationActivities: nil)
                present(avc, animated: true)
            } else {
                print(error)
            }
        } catch {
            print(error)
        }
    }

Conclusion

Your final code should look like the following.

import UIKit

class ViewController: UIViewController, UIDocumentPickerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func viewDidAppear(_ animated: Bool) {
        let docsTypes = ["public.data"]
        let documentPicker = UIDocumentPickerViewController(documentTypes: docsTypes,
                                                            in: .import)
        documentPicker.delegate = self
        documentPicker.allowsMultipleSelection = false
        self.present(documentPicker, animated: true, completion: nil)
    }
    

    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
        zip(sourceUrl: urls[0])
    }
    
    func zip(sourceUrl: URL) {
        let fm = FileManager.default
        var documentsUrl = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
        
        do {
            let fileUrl = documentsUrl.appendingPathComponent(sourceUrl.lastPathComponent)
            try? fm.removeItem(at: fileUrl)
            try fm.moveItem(at: sourceUrl, to: fileUrl)
            
            var archiveUrl: URL?
            var error: NSError?
            NSFileCoordinator().coordinate(readingItemAt: documentsUrl, options: [.forUploading], error: &error) { (zipUrl) in
                let tmpUrl = try! fm.url(for: .itemReplacementDirectory, in: .userDomainMask, appropriateFor: zipUrl, create: true
                ).appendingPathComponent("archive.zip")
                try! fm.moveItem(at: zipUrl, to: tmpUrl)
                archiveUrl = tmpUrl
            }

            if let archiveUrl = archiveUrl {
                let avc = UIActivityViewController(activityItems: [archiveUrl], applicationActivities: nil)
                present(avc, animated: true)
            } else {
                print(error)
            }
        } catch {
            print(error)
        }
    }
}

Thank you for reading this tutorial. Please leave any comments or questions below. You can also find the final code on GitHub.

Anders Schnell Avatar

Published by

Categories:

Leave a comment