Creating a ZIP file from a string in Swift

user2363025 picture user2363025 · Oct 18, 2017 · Viewed 8.1k times · Source
 let data = "InPractiseThisWillBeAReheallyLongString"
     
        createDir()
        
        let docsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let ourDir = docsDir.appendingPathComponent("ourCustomDir/")
        let tempDir = ourDir.appendingPathComponent("temp/")
        let unzippedDir = tempDir.appendingPathComponent("unzippedDir/")
        let unzippedfileDir = unzippedDir.appendingPathComponent("unZipped.txt")
        let zippedDir = tempDir.appendingPathComponent("Zipped.zip")
        do {
            
            try data.write(to: unzippedfileDir, atomically: false, encoding: .utf8)
            
            
            let x = SSZipArchive.createZipFile(atPath: zippedDir.path, withContentsOfDirectory: unzippedfileDir.path)
            
            var zipData: NSData! = NSData()
            
            do {
                zipData = try NSData(contentsOfFile: unzippedfileDir.path, options: NSData.ReadingOptions.mappedIfSafe)
                //once I get a readable .zip file, I will be using this zipData in a multipart webservice
            }
            catch let err as NSError {
                print("err 1 here is :\(err.localizedDescription)")
            }
        }
        catch let err as NSError {
            
            print("err 3 here is :\(err.localizedDescription)")
        }

And the createDir function is:

func createDir() {
        let docsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let ourDir = docsDir.appendingPathComponent("ourCustomDir/")
        let tempDir = ourDir.appendingPathComponent("temp/")
        let unzippedDir = tempDir.appendingPathComponent("unzippedDir/")
        let fileManager = FileManager.default
        if fileManager.fileExists(atPath: tempDir.path) {
            deleteFile(path: tempDir)
            deleteFile(path: unzippedDir)
        } else {
            print("file does not exist")
            do {
                try FileManager.default.createDirectory(atPath: tempDir.path, withIntermediateDirectories: true, attributes: nil)
                try FileManager.default.createDirectory(atPath: unzippedDir.path, withIntermediateDirectories: true, attributes: nil)
                print("creating dir \(tempDir)")
            } catch let error as NSError {
                print("here : " + error.localizedDescription)
            }
        }
    }

Right now I am not getting any errors but when I download my appData container, get the ZIP file and attempt to unzip, I ma told the ZIP file is empty. I can see that the unzipped.text file does exist as expected.

Any idea what I'm doing wrong?

Is there a method to create a .zip directly from the string without having to save the file to the data container?


UPDATE

I also tried the following and have the exact same results:

let zipArch = SSZipArchive(path: zippedDir.path)
        print(zipArch.open)
        print(zipArch.write(dataStr.data(using: String.Encoding.utf8)!, filename: "blah.txt", withPassword: ""))
        print(zipArch.close)

Answer

Thomas Zoechling picture Thomas Zoechling · Oct 20, 2017

You could use ZIPFoundation, which is another Swift ZIP library that allows you to read, create and modify ZIP files. One of its advantages is that it allows you to add ZIP entries "on-the-fly". You don't have to write your string to disk before creating an archive from it. It provides a closure based API where you can feed the string directly into a newly created archive:

func zipString() {
    let string = "InPractiseThisWillBeAReheallyLongString"
    var archiveURL = URL(fileURLWithPath: NSTemporaryDirectory())
    archiveURL.appendPathComponent(ProcessInfo.processInfo.globallyUniqueString)
    archiveURL.appendPathExtension("zip")
    guard let data = string.data(using: .utf8) else { return }
    guard let archive = Archive(url: archiveURL, accessMode: .create) else { return }

    try? archive.addEntry(with: "unZipped.txt", type: .file, uncompressedSize: UInt32(data.count), provider: { (position, size) -> Data in
        return data
    })
}

The addEntry method also has an optional bufferSize parameter that can be used to perform chunked addition (so that you don't have to load the whole data object into RAM.)