From cb0a0540b7a05498cbdcc21a585d01ad18242ebf Mon Sep 17 00:00:00 2001 From: Jake Welton Date: Thu, 9 Jun 2016 21:03:42 +0100 Subject: [PATCH] Add cancel operation feature --- Zip/Zip.swift | 28 ++++++++++++++---- ZipTests/ZipTests.swift | 65 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 5 deletions(-) diff --git a/Zip/Zip.swift b/Zip/Zip.swift index 544e3008..868e12be 100644 --- a/Zip/Zip.swift +++ b/Zip/Zip.swift @@ -17,13 +17,16 @@ public enum ZipError: Error { case unzipFail /// Zip fail case zipFail - + /// Operation Cancelled + case operationCancelled + /// User readable description public var description: String { switch self { case .fileNotFound: return NSLocalizedString("File not found.", comment: "") case .unzipFail: return NSLocalizedString("Failed to unzip file.", comment: "") case .zipFail: return NSLocalizedString("Failed to zip file.", comment: "") + case .operationCancelled: return NSLocalizedString("Operation cancelled", comment: "") } } } @@ -50,12 +53,16 @@ public enum ZipCompression: Int { /// Zip class public class Zip { - /** Set of vaild file extensions */ internal static var customFileExtensions: Set = [] + /** + Cancellation block. This is set once the progress tracker is has been created + */ + public static var cancelCurrentOperation: (()->Void) = { } + // MARK: Lifecycle /** @@ -113,6 +120,8 @@ public class Zip { progressTracker.isPausable = false progressTracker.kind = ProgressKind.file + cancelCurrentOperation = { progressTracker.cancel() } + // Begin unzipping let zip = unzOpen64(path) defer { @@ -234,8 +243,12 @@ public class Zip { } progressTracker.completedUnitCount = Int64(currentPosition) - - } while (ret == UNZ_OK && ret != UNZ_END_OF_LIST_OF_FILE) + + } while (ret == UNZ_OK && ret != UNZ_END_OF_LIST_OF_FILE) && !progressTracker.isCancelled + + guard !progressTracker.isCancelled else { + throw ZipError.operationCancelled + } // Completed. Update progress handler. if let progressHandler = progress{ @@ -243,7 +256,6 @@ public class Zip { } progressTracker.completedUnitCount = Int64(totalSize) - } // MARK: Zip @@ -297,6 +309,8 @@ public class Zip { progressTracker.isPausable = false progressTracker.kind = ProgressKind.file + cancelCurrentOperation = { progressTracker.cancel() } + // Begin Zipping let zip = zipOpen(destinationPath, APPEND_STATUS_CREATE) for path in processedPaths { @@ -338,6 +352,10 @@ public class Zip { } var length: Int = 0 while (feof(input) == 0) { + guard !progressTracker.isCancelled else { + throw ZipError.operationCancelled + } + length = fread(buffer, 1, chunkSize, input) zipWriteInFileInZip(zip, buffer, UInt32(length)) } diff --git a/ZipTests/ZipTests.swift b/ZipTests/ZipTests.swift index 282a3e36..d140ea7a 100644 --- a/ZipTests/ZipTests.swift +++ b/ZipTests/ZipTests.swift @@ -315,4 +315,69 @@ class ZipTests: XCTestCase { XCTAssertTrue(Zip.isValidFileExtension("cbz")) } + func testCancelDuringZipCancels() { + var currentProgress: Double = 0 + + let expect = expectation(description: "Zip operation should throw .OperationCancelled") + + do { + let progress = Progress() + progress.totalUnitCount = 1 + + let imageURL1 = Bundle(for: ZipTests.self).url(forResource: "3crBXeO", withExtension: "gif")! + let imageURL2 = Bundle(for: ZipTests.self).url(forResource: "kYkLkPf", withExtension: "gif")! + let documentsFolder = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as NSURL + let zipFilePath = documentsFolder.appendingPathComponent("archive.zip")! + + try Zip.zipFiles(paths: [imageURL1, imageURL2], zipFilePath: zipFilePath, password: nil) { progress in + currentProgress = progress + + if progress > 0 { + Zip.cancelCurrentOperation() + } + } + + } + catch { + XCTAssertEqual(error as? ZipError, .operationCancelled) + XCTAssertLessThan(currentProgress, 1, "Progress should be less than 1 when cancelling the current operation") + expect.fulfill() + } + + waitForExpectations(timeout: 5) { error in + XCTAssertNil(error) + } + } + + func testCancelDuringUnzipCancels() { + var currentProgress: Double = 0 + let expect = expectation(description: "Unzip operation should throw .OperationCancelled") + + do { + let progress = Progress() + progress.totalUnitCount = 1 + + let bookURL = Bundle(for: ZipTests.self).url(forResource: "bb8", withExtension: "zip")! + let documentsFolder = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as NSURL + let zipFilePath = documentsFolder.appendingPathComponent("contents")! + + try Zip.unzipFile(bookURL, destination: zipFilePath, overwrite: true, password: nil, progress: { progress in + currentProgress = progress + + if progress > 0 { + Zip.cancelCurrentOperation() + } + }) { _ in } + } + catch { + XCTAssertEqual(error as? ZipError, .operationCancelled) + XCTAssertLessThan(currentProgress, 1, "Progress should be less than 1 when cancelling the current operation") + expect.fulfill() + } + + waitForExpectations(timeout: 5) { error in + XCTAssertNil(error) + } + } + }