diff --git a/.travis.yml b/.travis.yml index 3434eee..e4b3026 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,11 @@ language: - objective-c -osx_image: xcode9.3 +osx_image: xcode10.2 before_script: + # Only until CocoaPods 1.8.0 beta 1 is released. Then remove this and the Gemfile + - bundle config github.https true && bundle install # Make log level less verbose. Temporarily undo if more info is needed - sudo log config --mode "level:default" diff --git a/CHANGELOG.md b/CHANGELOG.md index 371e61c..a9bdf5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # UnzipKit CHANGELOG +## 2.0 + +* Removed methods deprecated in v1.9 (Issue #90, PR #92) +* Fixed behavior of `-extractFilesTo:overwrite:error:`, so it shows the progress of each individual file as they extract (Issue #91, PR #94) +* Deprecated the initializers that take a file path instead of an `NSURL` (Issue #90, PR #95) + ## 1.9 * Added support for `NSProgress` and `NSProgressReporting` in all extraction and iteration methods (Issue #32) diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..5d65347 --- /dev/null +++ b/Gemfile @@ -0,0 +1,6 @@ +source 'https://rubygems.org' + +gem 'cocoapods', :github => 'CocoaPods/CocoaPods' +gem 'cocoapods-core', :github => 'CocoaPods/Core' +gem 'cocoapods-trunk', :github => 'CocoaPods/cocoapods-trunk' +gem 'xcodeproj', :github => 'CocoaPods/Xcodeproj' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..fd69ae2 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,105 @@ +GIT + remote: https://github.com/CocoaPods/CocoaPods.git + revision: b93c72d3d68b5075435354de7cb2116579f02c8e + specs: + cocoapods (1.7.5) + activesupport (>= 4.0.2, < 5) + claide (>= 1.0.2, < 2.0) + cocoapods-core (= 1.7.5) + cocoapods-deintegrate (>= 1.0.3, < 2.0) + cocoapods-downloader (>= 1.2.2, < 2.0) + cocoapods-plugins (>= 1.0.0, < 2.0) + cocoapods-search (>= 1.0.0, < 2.0) + cocoapods-stats (>= 1.0.0, < 2.0) + cocoapods-trunk (>= 1.3.1, < 2.0) + cocoapods-try (>= 1.1.0, < 2.0) + colored2 (~> 3.1) + escape (~> 0.0.4) + fourflusher (>= 2.3.0, < 3.0) + gh_inspector (~> 1.0) + molinillo (~> 0.6.6) + nap (~> 1.0) + ruby-macho (~> 1.4) + xcodeproj (>= 1.10.0, < 2.0) + +GIT + remote: https://github.com/CocoaPods/Core.git + revision: 9ae4051d5fedf5869b603b1ac409a384537abdf4 + specs: + cocoapods-core (1.7.5) + activesupport (>= 4.0.2, < 6) + algoliasearch (~> 1.0) + fuzzy_match (~> 2.0.4) + nap (~> 1.0) + +GIT + remote: https://github.com/CocoaPods/Xcodeproj.git + revision: 5b4e8ef7bf54b5d5cbd8970b92cbb9d99f58a942 + specs: + xcodeproj (1.11.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.2.6) + +GIT + remote: https://github.com/CocoaPods/cocoapods-trunk.git + revision: 208396fca7eeecf4b7fb2810f1a1ab3b036bd1a3 + specs: + cocoapods-trunk (1.3.1) + nap (>= 0.8, < 2.0) + netrc (~> 0.11) + +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.0) + activesupport (4.2.11.1) + i18n (~> 0.7) + minitest (~> 5.1) + thread_safe (~> 0.3, >= 0.3.4) + tzinfo (~> 1.1) + algoliasearch (1.26.0) + httpclient (~> 2.8, >= 2.8.3) + json (>= 1.5.1) + atomos (0.1.3) + claide (1.0.2) + cocoapods-deintegrate (1.0.4) + cocoapods-downloader (1.2.2) + cocoapods-plugins (1.0.0) + nap + cocoapods-search (1.0.0) + cocoapods-stats (1.1.0) + cocoapods-try (1.1.0) + colored2 (3.1.2) + concurrent-ruby (1.1.5) + escape (0.0.4) + fourflusher (2.3.1) + fuzzy_match (2.0.4) + gh_inspector (1.1.3) + httpclient (2.8.3) + i18n (0.9.5) + concurrent-ruby (~> 1.0) + json (2.2.0) + minitest (5.11.3) + molinillo (0.6.6) + nanaimo (0.2.6) + nap (1.1.0) + netrc (0.11.0) + ruby-macho (1.4.0) + thread_safe (0.3.6) + tzinfo (1.2.5) + thread_safe (~> 0.1) + +PLATFORMS + ruby + +DEPENDENCIES + cocoapods! + cocoapods-core! + cocoapods-trunk! + xcodeproj! + +BUNDLED WITH + 2.0.2 diff --git a/Scripts/cocoapod-validate.sh b/Scripts/cocoapod-validate.sh index 57fcc73..5e82410 100755 --- a/Scripts/cocoapod-validate.sh +++ b/Scripts/cocoapod-validate.sh @@ -5,12 +5,12 @@ set -o pipefail . Scripts/set-travis-tag-to-latest.sh -pod env +bundle exec pod env # Lint the podspec to check for errors. Don't call `pod spec lint`, because we want it to evaluate locally # Using sed to remove logging from output until CocoaPods issue #7577 is implemented and I can use the # OS_ACTIVITY_MODE = disable environment variable from the test spec scheme -pod lib lint --verbose | sed -l '/xctest\[/d; /^$/d' +bundle exec pod lib lint --verbose | sed -l '/xctest\[/d; /^$/d' . Scripts/unset-travis-tag.sh \ No newline at end of file diff --git a/Scripts/install-demo-libs.sh b/Scripts/install-demo-libs.sh index 64982a0..8907a6b 100755 --- a/Scripts/install-demo-libs.sh +++ b/Scripts/install-demo-libs.sh @@ -5,8 +5,8 @@ set -ev . Scripts/set-travis-tag-to-latest.sh pushd UnzipKitDemo -pod --version -pod update +bundle exec pod --version +bundle exec pod update popd . Scripts/unset-travis-tag.sh \ No newline at end of file diff --git a/Source/UZKArchive.h b/Source/UZKArchive.h index ce99538..81c4619 100644 --- a/Source/UZKArchive.h +++ b/Source/UZKArchive.h @@ -182,50 +182,11 @@ extern NSString *UZKErrorDomain; * DEPRECATED: Creates and returns an archive at the given path * * @param filePath A path to the archive file - * - * @return Returns a UZKArchive object, or nil if the path isn't reachable - */ -+ (nullable instancetype)zipArchiveAtPath:(NSString *)filePath __deprecated_msg("Use -initWithPath:error: instead"); - -/** - * DEPRECATED: Creates and returns an archive at the given URL - * - * @param fileURL The URL of the archive file - * - * @return Returns a UZKArchive object, or nil if the URL isn't reachable - */ -+ (nullable instancetype)zipArchiveAtURL:(NSURL *)fileURL __deprecated_msg("Use -initWithURL:error: instead"); - -/** - * DEPRECATED: Creates and returns an archive at the given path, with a given password - * - * @param filePath A path to the archive file - * @param password The password of the given archive - * - * @return Returns a UZKArchive object, or nil if the path isn't reachable - */ -+ (nullable instancetype)zipArchiveAtPath:(NSString *)filePath password:(nullable NSString *)password __deprecated_msg("Use -initWithPath:password:error: instead"); - -/** - * DEPRECATED: Creates and returns an archive at the given URL, with a given password - * - * @param fileURL The URL of the archive file - * @param password The password of the given archive - * - * @return Returns a UZKArchive object, or nil if the URL isn't reachable - */ -+ (nullable instancetype)zipArchiveAtURL:(NSURL *)fileURL password:(nullable NSString *)password __deprecated_msg("Use -initWithURL:password:error: instead");; - - -/** - * Creates and returns an archive at the given path - * - * @param filePath A path to the archive file * @param error Returns an error code if the object can't be initialized * * @return Returns a UZKArchive object, or nil if the path isn't reachable */ -- (nullable instancetype)initWithPath:(NSString *)filePath error:(NSError **)error; +- (nullable instancetype)initWithPath:(NSString *)filePath error:(NSError **)error __deprecated_msg("Use -initWithURL:error: instead"); /** * Creates and returns an archive at the given URL @@ -238,7 +199,7 @@ extern NSString *UZKErrorDomain; - (nullable instancetype)initWithURL:(NSURL *)fileURL error:(NSError **)error; /** - * Creates and returns an archive at the given path, with a given password + * DEPRECATED: Creates and returns an archive at the given path, with a given password * * @param filePath A path to the archive file * @param password The password of the given archive @@ -246,7 +207,7 @@ extern NSString *UZKErrorDomain; * * @return Returns a UZKArchive object, or nil if the path isn't reachable */ -- (nullable instancetype)initWithPath:(NSString *)filePath password:(nullable NSString *)password error:(NSError **)error; +- (nullable instancetype)initWithPath:(NSString *)filePath password:(nullable NSString *)password error:(NSError **)error __deprecated_msg("Use -initWithURL:password:error: instead"); /** * Creates and returns an archive at the given URL, with a given password @@ -318,25 +279,6 @@ extern NSString *UZKErrorDomain; overwrite:(BOOL)overwrite error:(NSError **)error; -/** - * **DEPRECATED:** Writes all files in the archive to the given path - * - * @param destinationDirectory The destination path of the unarchived files - * @param overwrite YES to overwrite files in the destination directory, NO otherwise - * @param progress Called every so often to report the progress of the extraction - * - * - *currentFile* The info about the file that's being extracted - * - *percentArchiveDecompressed* The percentage of the archive that has been decompressed - * - * @param error Contains an NSError object when there was an error reading the archive - * - * @return YES on successful extraction, NO if an error was encountered - */ -- (BOOL)extractFilesTo:(NSString *)destinationDirectory - overwrite:(BOOL)overwrite - progress:(nullable void (^)(UZKFileInfo *currentFile, CGFloat percentArchiveDecompressed))progress - error:(NSError **)error __deprecated_msg("Use -extractFilesTo:overwrite:error: instead, and if using the progress block, replace with NSProgress as described in the README"); - /** * Unarchive a single file from the archive into memory. Supports NSProgress for progress reporting, which also * allows cancellation in the middle of extraction @@ -349,22 +291,6 @@ extern NSString *UZKErrorDomain; - (nullable NSData *)extractData:(UZKFileInfo *)fileInfo error:(NSError **)error; -/** - * **DEPRECATED:** Unarchive a single file from the archive into memory - * - * @param fileInfo The info of the file within the archive to be expanded. Only the filename property is used - * @param progress Called every so often to report the progress of the extraction - * - * - *percentDecompressed* The percentage of the archive that has been decompressed - * - * @param error Contains an NSError object when there was an error reading the archive - * - * @return An NSData object containing the bytes of the file, or nil if an error was encountered - */ -- (nullable NSData *)extractData:(UZKFileInfo *)fileInfo - progress:(nullable void (^)(CGFloat percentDecompressed))progress - error:(NSError **)error __deprecated_msg("Use -extractData:error: instead, and if using the progress block, replace with NSProgress as described in the README"); - /** * Unarchive a single file from the archive into memory. Supports NSProgress for progress reporting, which also * allows cancellation in the middle of extraction @@ -377,22 +303,6 @@ extern NSString *UZKErrorDomain; - (nullable NSData *)extractDataFromFile:(NSString *)filePath error:(NSError **)error; -/** - * **DEPRECATED:** Unarchive a single file from the archive into memory - * - * @param filePath The path of the file within the archive to be expanded - * @param progress Called every so often to report the progress of the extraction - * - * - *percentDecompressed* The percentage of the file that has been decompressed - * - * @param error Contains an NSError object when there was an error reading the archive - * - * @return An NSData object containing the bytes of the file, or nil if an error was encountered - */ -- (nullable NSData *)extractDataFromFile:(NSString *)filePath - progress:(nullable void (^)(CGFloat percentDecompressed))progress - error:(NSError **)error __deprecated_msg("Use -extractDataFromFile:error: instead, and if using the progress block, replace with NSProgress as described in the README"); - /** * Loops through each file in the archive into memory, allowing you to perform an action * using its info. Supports NSProgress for progress reporting, which also @@ -495,25 +405,6 @@ extern NSString *UZKErrorDomain; filePath:(NSString *)filePath error:(NSError **)error; -/** - * **DEPRECATED:** Writes the data to the zip file, overwriting it if a file of that name already exists in the - * archive - * - * @param data Data to write into the archive - * @param filePath The full path to the target file in the archive - * @param progress Called every so often to report the progress of the compression - * - * - *percentCompressed* The percentage of the file that has been compressed - * - * @param error Contains an NSError object when there was an error writing to the archive - * - * @return YES if successful, NO on error - */ -- (BOOL)writeData:(NSData *)data - filePath:(NSString *)filePath - progress:(nullable void (^)(CGFloat percentCompressed))progress - error:(NSError **)error __deprecated_msg("Use -writeData:filePath:error: instead, and if using the progress block, replace with NSProgress as described in the README"); - /** * Writes the data to the zip file, overwriting it if a file of that name already exists in the archive * @@ -529,26 +420,6 @@ extern NSString *UZKErrorDomain; fileDate:(nullable NSDate *)fileDate error:(NSError **)error; -/** - * **DEPRECATED:** Writes the data to the zip file, overwriting it if a file of that name already exists in the archive - * - * @param data Data to write into the archive - * @param filePath The full path to the target file in the archive - * @param fileDate The timestamp of the file in the archive. Uses the current time if nil - * @param progress Called every so often to report the progress of the compression - * - * - *percentCompressed* The percentage of the file that has been compressed - * - * @param error Contains an NSError object when there was an error writing to the archive - * - * @return YES if successful, NO on error - */ -- (BOOL)writeData:(NSData *)data - filePath:(NSString *)filePath - fileDate:(nullable NSDate *)fileDate - progress:(nullable void (^)(CGFloat percentCompressed))progress - error:(NSError **)error __deprecated_msg("Use -writeData:filePath:fileDate:error: instead, and if using the progress block, replace with NSProgress as described in the README"); - /** * Writes the data to the zip file, overwriting it if a file of that name already exists in the archive * @@ -568,30 +439,6 @@ compressionMethod:(UZKCompressionMethod)method password:(nullable NSString *)password error:(NSError **)error; -/** - * **DEPRECATED:** Writes the data to the zip file, overwriting it if a file of that name already exists in the archive - * - * @param data Data to write into the archive - * @param filePath The full path to the target file in the archive - * @param fileDate The timestamp of the file in the archive. Uses the current time if nil - * @param method The UZKCompressionMethod to use (Default, None, Fastest, Best) - * @param password Override the password associated with the archive (not recommended) - * @param progress Called every so often to report the progress of the compression - * - * - *percentCompressed* The percentage of the file that has been compressed - * - * @param error Contains an NSError object when there was an error writing to the archive - * - * @return YES if successful, NO on error - */ -- (BOOL)writeData:(NSData *)data - filePath:(NSString *)filePath - fileDate:(nullable NSDate *)fileDate -compressionMethod:(UZKCompressionMethod)method - password:(nullable NSString *)password - progress:(nullable void (^)(CGFloat percentCompressed))progress - error:(NSError **)error __deprecated_msg("Use -writeData:filePath:fileDate:compressionMethod:password:error: instead, and if using the progress block, replace with NSProgress as described in the README"); - /** * Writes the data to the zip file, overwriting only if specified with the overwrite flag. Overwriting * presents a tradeoff: the whole archive needs to be copied (minus the file to be overwritten) before @@ -646,70 +493,6 @@ compressionMethod:(UZKCompressionMethod)method overwrite:(BOOL)overwrite error:(NSError **)error; -/** - * **DEPRECATED:** Writes the data to the zip file, overwriting only if specified with the overwrite flag. Overwriting - * presents a tradeoff: the whole archive needs to be copied (minus the file to be overwritten) before - * the write begins. For a large archive, this can be slow. On the other hand, when not overwriting, - * the size of the archive will grow each time the file is written. - * - * @param data Data to write into the archive - * @param filePath The full path to the target file in the archive - * @param fileDate The timestamp of the file in the archive. Uses the current time if nil - * @param method The UZKCompressionMethod to use (Default, None, Fastest, Best) - * @param password Override the password associated with the archive (not recommended) - * @param overwrite If YES, and the file exists, delete it before writing. If NO, append - * the data into the archive without removing it first (legacy Objective-Zip - * behavior) - * @param progress Called every so often to report the progress of the compression - * - * - *percentCompressed* The percentage of the file that has been compressed - * - * @param error Contains an NSError object when there was an error writing to the archive - * - * @return YES if successful, NO on error - */ -- (BOOL)writeData:(NSData *)data - filePath:(NSString *)filePath - fileDate:(nullable NSDate *)fileDate -compressionMethod:(UZKCompressionMethod)method - password:(nullable NSString *)password - overwrite:(BOOL)overwrite - progress:(nullable void (^)(CGFloat percentCompressed))progress - error:(NSError **)error __deprecated_msg("Use -writeData:filePath:fileDate:compressionMethod:password:overwrite:error: instead, and if using the progress block, replace with NSProgress as described in the README"); - -/** - * **DEPRECATED:** Writes the data to the zip file, overwriting only if specified with the overwrite flag. Overwriting - * presents a tradeoff: the whole archive needs to be copied (minus the file to be overwritten) before - * the write begins. For a large archive, this can be slow. On the other hand, when not overwriting, - * the size of the archive will grow each time the file is written. - * - * @param data Data to write into the archive - * @param filePath The full path to the target file in the archive - * @param fileDate The timestamp of the file in the archive. Uses the current time if nil - * @param permissions The desired POSIX permissions of the file in the archive - * @param method The UZKCompressionMethod to use (Default, None, Fastest, Best) - * @param password Override the password associated with the archive (not recommended) - * @param overwrite If YES, and the file exists, delete it before writing. If NO, append - * the data into the archive without removing it first (legacy Objective-Zip - * behavior) - * @param progress Called every so often to report the progress of the compression - * - * - *percentCompressed* The percentage of the file that has been compressed - * - * @param error Contains an NSError object when there was an error writing to the archive - * - * @return YES if successful, NO on error - */ -- (BOOL)writeData:(NSData *)data - filePath:(NSString *)filePath - fileDate:(nullable NSDate *)fileDate - posixPermissions:(short)permissions -compressionMethod:(UZKCompressionMethod)method - password:(nullable NSString *)password - overwrite:(BOOL)overwrite - progress:(nullable void (^)(CGFloat percentCompressed))progress - error:(NSError **)error __deprecated_msg("Use -writeData:filePath:fileDate:permissions:compressionMethod:password:overwrite:error: instead, and if using the progress block, replace with NSProgress as described in the README"); - /** * Writes data to the zip file in pieces, allowing you to stream the write, so the entire contents * don't need to reside in memory at once. It overwrites an existing file with the same name. diff --git a/Source/UZKArchive.m b/Source/UZKArchive.m index a8ac32a..44208c4 100644 --- a/Source/UZKArchive.m +++ b/Source/UZKArchive.m @@ -69,31 +69,6 @@ @implementation UZKArchive @synthesize comment = _comment; -#pragma mark - Deprecated Convenience Methods - - -+ (UZKArchive *)zipArchiveAtPath:(NSString *)filePath -{ - return [[UZKArchive alloc] initWithPath:filePath error:nil]; -} - -+ (UZKArchive *)zipArchiveAtURL:(NSURL *)fileURL -{ - return [[UZKArchive alloc] initWithURL:fileURL error:nil]; -} - -+ (UZKArchive *)zipArchiveAtPath:(NSString *)filePath password:(NSString *)password -{ - return [[UZKArchive alloc] initWithPath:filePath password:password error:nil]; -} - -+ (UZKArchive *)zipArchiveAtURL:(NSURL *)fileURL password:(NSString *)password -{ - return [[UZKArchive alloc] initWithURL:fileURL password:password error:nil]; -} - - - #pragma mark - Initializers + (void)initialize { @@ -468,20 +443,6 @@ + (BOOL)urlIsAZip:(NSURL *)fileURL - (BOOL)extractFilesTo:(NSString *)destinationDirectory overwrite:(BOOL)overwrite error:(NSError * __autoreleasing*)error -{ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - return [self extractFilesTo:destinationDirectory - overwrite:overwrite - progress:nil - error:error]; -#pragma clang diagnostic pop -} - -- (BOOL)extractFilesTo:(NSString *)destinationDirectory - overwrite:(BOOL)overwrite - progress:(void (^)(UZKFileInfo *currentFile, CGFloat percentArchiveDecompressed))progressBlock - error:(NSError * __autoreleasing*)error { UZKCreateActivity("Extracting Files to Directory"); @@ -530,10 +491,6 @@ - (BOOL)extractFilesTo:(NSString *)destinationDirectory } @autoreleasepool { - if (progressBlock) { - progressBlock(info, bytesDecompressed / totalSize.doubleValue); - } - if (![self locateFileInZip:info.filename error:&strongError]) { NSString *detail = [NSString localizedStringWithFormat:NSLocalizedStringFromTableInBundle(@"Error locating file '%@' in archive", @"UnzipKit", _resources, @"Detailed error string"), info.filename]; @@ -610,6 +567,8 @@ - (BOOL)extractFilesTo:(NSString *)destinationDirectory return; } + [progress becomeCurrentWithPendingUnitCount:info.uncompressedSize]; + UZKLogDebug("Extracting buffered data"); BOOL extractSuccess = [welf extractBufferedDataFromFile:info.filename error:&strongError @@ -618,10 +577,9 @@ - (BOOL)extractFilesTo:(NSString *)destinationDirectory UZKLogDebug("Writing data chunk of size %lu (%lld total so far)", (unsigned long)dataChunk.length, bytesDecompressed); bytesDecompressed += dataChunk.length; [deflatedFileHandle writeData:dataChunk]; - if (progressBlock) { - progressBlock(info, (double)bytesDecompressed / totalSize.doubleValue); - } }]; + + [progress resignCurrent]; UZKLogDebug("Closing file handle"); [deflatedFileHandle closeFile]; @@ -645,7 +603,6 @@ - (BOOL)extractFilesTo:(NSString *)destinationDirectory forKey:NSProgressFileCompletedCountKey]; [progress setUserInfoObject:@(fileInfo.count) forKey:NSProgressFileTotalCountKey]; - progress.completedUnitCount = bytesDecompressed; } } } @@ -670,29 +627,8 @@ - (nullable NSData *)extractData:(UZKFileInfo *)fileInfo error:error]; } -- (nullable NSData *)extractData:(UZKFileInfo *)fileInfo - progress:(void (^)(CGFloat))progress - error:(NSError * __autoreleasing*)error -{ - return [self extractDataFromFile:fileInfo.filename - progress:progress - error:error]; -} - - (nullable NSData *)extractDataFromFile:(NSString *)filePath error:(NSError * __autoreleasing *)error -{ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - return [self extractDataFromFile:filePath - progress:nil - error:error]; -#pragma clang diagnostic pop -} - -- (nullable NSData *)extractDataFromFile:(NSString *)filePath - progress:(void (^)(CGFloat))progressBlock - error:(NSError * __autoreleasing*)error { UZKCreateActivity("Extracting Data from File"); @@ -705,19 +641,9 @@ - (nullable NSData *)extractDataFromFile:(NSString *)filePath error:&extractError action:^(NSData *dataChunk, CGFloat percentDecompressed) { UZKLogDebug("Appending data chunk of size %lu (%.3f%% complete)", (unsigned long)dataChunk.length, (double)percentDecompressed * 100); - - if (progressBlock) { - progressBlock(percentDecompressed); - } - [result appendData:dataChunk]; }]; - if (progressBlock) { - UZKLogDebug("Declaring extraction progress as completed"); - progressBlock(1.0); - } - if (success) { return [NSData dataWithData:result]; } @@ -1066,21 +992,6 @@ - (BOOL)writeData:(NSData *)data error:error]; } -- (BOOL)writeData:(NSData *)data - filePath:(NSString *)filePath - progress:(void (^)(CGFloat percentCompressed))progress - error:(NSError * __autoreleasing*)error -{ - return [self writeData:data - filePath:filePath - fileDate:nil - compressionMethod:UZKCompressionMethodDefault - password:nil - overwrite:YES - progress:progress - error:error]; -} - - (BOOL)writeData:(NSData *)data filePath:(NSString *)filePath fileDate:(NSDate *)fileDate @@ -1095,44 +1006,11 @@ - (BOOL)writeData:(NSData *)data error:error]; } -- (BOOL)writeData:(NSData *)data - filePath:(NSString *)filePath - fileDate:(NSDate *)fileDate - progress:(void (^)(CGFloat percentCompressed))progress - error:(NSError * __autoreleasing*)error -{ - return [self writeData:data - filePath:filePath - fileDate:fileDate - compressionMethod:UZKCompressionMethodDefault - password:nil - overwrite:YES - progress:progress - error:error]; -} - -- (BOOL)writeData:(NSData *)data - filePath:(NSString *)filePath - fileDate:(NSDate *)fileDate -compressionMethod:(UZKCompressionMethod)method - password:(NSString *)password - error:(NSError * __autoreleasing*)error -{ - return [self writeData:data - filePath:filePath - fileDate:fileDate - compressionMethod:method - password:password - overwrite:YES - error:error]; -} - - (BOOL)writeData:(NSData *)data filePath:(NSString *)filePath fileDate:(NSDate *)fileDate compressionMethod:(UZKCompressionMethod)method password:(NSString *)password - progress:(void (^)(CGFloat percentCompressed))progress error:(NSError * __autoreleasing*)error { return [self writeData:data @@ -1141,29 +1019,7 @@ - (BOOL)writeData:(NSData *)data compressionMethod:method password:password overwrite:YES - progress:progress - error:error]; -} - -- (BOOL)writeData:(NSData *)data - filePath:(NSString *)filePath - fileDate:(NSDate *)fileDate -compressionMethod:(UZKCompressionMethod)method - password:(NSString *)password - overwrite:(BOOL)overwrite - error:(NSError * __autoreleasing*)error -{ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - return [self writeData:data - filePath:filePath - fileDate:fileDate - compressionMethod:method - password:password - overwrite:overwrite - progress:nil error:error]; -#pragma clang diagnostic pop } - (BOOL)writeData:(NSData *)data @@ -1172,7 +1028,6 @@ - (BOOL)writeData:(NSData *)data compressionMethod:(UZKCompressionMethod)method password:(NSString *)password overwrite:(BOOL)overwrite - progress:(void (^)(CGFloat percentCompressed))progressBlock error:(NSError * __autoreleasing*)error { return [self writeData:data @@ -1182,31 +1037,7 @@ - (BOOL)writeData:(NSData *)data compressionMethod:method password:password overwrite:overwrite - progress:progressBlock - error:error]; -} - -- (BOOL)writeData:(NSData *)data - filePath:(NSString *)filePath - fileDate:(nullable NSDate *)fileDate - posixPermissions:(short)permissions -compressionMethod:(UZKCompressionMethod)method - password:(nullable NSString *)password - overwrite:(BOOL)overwrite - error:(NSError * __autoreleasing*)error -{ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - return [self writeData:data - filePath:filePath - fileDate:fileDate - posixPermissions:permissions - compressionMethod:method - password:password - overwrite:overwrite - progress:nil error:error]; -#pragma clang diagnostic pop } - (BOOL)writeData:(NSData *)data @@ -1216,15 +1047,14 @@ - (BOOL)writeData:(NSData *)data compressionMethod:(UZKCompressionMethod)method password:(NSString *)password overwrite:(BOOL)overwrite - progress:(void (^)(CGFloat percentCompressed))progressBlock error:(NSError * __autoreleasing*)error { UZKCreateActivity("Writing Data"); UZKLogInfo("Writing data to archive. filePath: %{public}@, fileDate: %{time_t}ld, compressionMethod: %ld, password: %{public}@, " - "overwrite: %{public}@, progress block specified: %{public}@, error pointer specified: %{public}@", + "overwrite: %{public}@, error pointer specified: %{public}@", filePath, lrint(fileDate.timeIntervalSince1970), (long)method, password != nil ? @"" : @"(null)", overwrite ? @"YES" : @"NO", - progressBlock ? @"YES" : @"NO", error ? @"YES" : @"NO"); + error ? @"YES" : @"NO"); const NSUInteger bufferSize = 4096; //Arbitrary const void *bytes = data.bytes; @@ -1232,11 +1062,6 @@ - (BOOL)writeData:(NSData *)data NSProgress *progress = [self beginProgressOperation:data.length]; progress.cancellable = NO; - if (progressBlock) { - UZKLogDebug("Calling progress block with zero"); - progressBlock(0); - } - __weak UZKArchive *welf = self; uLong calculatedCRC = crc32(0, data.bytes, (uInt)data.length); UZKLogDebug("Calculated CRC: %010lu", calculatedCRC); @@ -1262,12 +1087,6 @@ - (BOOL)writeData:(NSData *)data } progress.completedUnitCount += size; - - if (progressBlock) { - double percentComplete = i / (double)data.length; - UZKLogDebug("Calling progress block at %.3f%%", percentComplete * 100); - progressBlock(percentComplete); - } } return ZIP_OK; diff --git a/Source/UZKFileInfo.h b/Source/UZKFileInfo.h index e14248e..898b67d 100644 --- a/Source/UZKFileInfo.h +++ b/Source/UZKFileInfo.h @@ -13,7 +13,7 @@ /** * Defines the various compression levels that can be applied to a file */ -typedef NS_ENUM(NSInteger, UZKCompressionMethod) { +typedef NS_ENUM(int, UZKCompressionMethod) { /** * Default level */ @@ -73,6 +73,11 @@ typedef NS_ENUM(NSInteger, UZKCompressionMethod) { */ @property (readonly) BOOL isDirectory; +/** + * YES if the file is a symbolic link + */ +@property (readonly) BOOL isSymbolicLink; + /** * The type of compression */ diff --git a/Source/UZKFileInfo.m b/Source/UZKFileInfo.m index 050adf6..4503313 100644 --- a/Source/UZKFileInfo.m +++ b/Source/UZKFileInfo.m @@ -7,6 +7,50 @@ #import "UZKFileInfo.h" #import "unzip.h" +/** + * Define the file storage system in zip according to "version made by" + * These values are taken from the PKWARE zip zpec, section 4.4.2.1 + * + * https://www.pkware.com/documents/casestudies/APPNOTE.TXT) + */ +typedef NS_ENUM(NSUInteger, UZKZipOS) { + UZKZipOSMSDOS = 0, + UZKZipOSAmiga = 1, + UZKZipOSOpenVMS = 2, + UZKZipOSUnix = 3, + UZKZipOSVMCMS = 4, + UZKZipOSAtariST = 5, + UZKZipOSOS2 = 6, + UZKZipOSClassicMac = 7, + UZKZipOSZSystem = 8, + UZKZipOSCPM = 9, + UZKZipOSWindowsNT = 10, + UZKZipOSMVS = 11, + UZKZipOSVSE = 12, + UZKZipOSAcorn = 13, + UZKZipOSVFAT = 14, + UZKZipOSAlternateMVS = 15, + UZKZipOSBeOS = 16, + UZKZipOSTandem = 17, + UZKZipOSOS400 = 18, + UZKZipOSDarwinOSX = 19 +}; + +/** + * These are the UNIX file types, as defined in system headers. These values are reused by the ZIP + * format's external file attributes (see especially Directory, Regular, SymLink) + */ +typedef NS_ENUM(ushort, UZKFileType) { + UZKFileTypeNamedPipe = S_IFIFO, + UZKFileTypeCharacterSpecial = S_IFCHR, + UZKFileTypeDirectory = S_IFDIR, + UZKFileTypeBlockSpecial = S_IFBLK, + UZKFileTypeRegular = S_IFREG, + UZKFileTypeSymLink = S_IFLNK, + UZKFileTypeSocket = S_IFSOCK +}; + + @interface UZKFileInfo () @property (readwrite) tm_unz zipTMUDate; @@ -19,6 +63,7 @@ @implementation UZKFileInfo @synthesize timestamp = _timestamp; + #pragma mark - Initialization @@ -35,7 +80,8 @@ - (instancetype)initWithFileInfo:(unz_file_info64 *)fileInfo filename:(NSString _zipTMUDate = fileInfo->tmu_date; _CRC = fileInfo->crc; _isEncryptedWithPassword = (fileInfo->flag & 1) != 0; - _isDirectory = [filename hasSuffix:@"/"]; + _isDirectory = [UZKFileInfo itemIsDirectory:fileInfo]; + _isSymbolicLink = [UZKFileInfo itemIsSymbolicLink:fileInfo]; if (_isDirectory) { _filename = [_filename substringToIndex:_filename.length - 1]; @@ -44,7 +90,7 @@ - (instancetype)initWithFileInfo:(unz_file_info64 *)fileInfo filename:(NSString _compressionMethod = [self readCompressionMethod:fileInfo->compression_method flag:fileInfo->flag]; - uLong permissions = (fileInfo->external_fa >> 16) & 0777U; + uLong permissions = [UZKFileInfo itemPermissions:fileInfo]; _posixPermissions = permissions ? permissions : 0644U; } return self; @@ -65,6 +111,41 @@ - (NSDate *)timestamp { +#pragma mark - Private Class Methods + + ++ (UZKZipOS)itemOS:(unz_file_info64 *)fileInfo { + return fileInfo->version >> 8; +} + ++ (UZKFileType)itemFileType:(unz_file_info64 *)fileInfo { + return S_IFMT & (fileInfo->external_fa >> 16); +} + ++ (uLong)itemPermissions:(unz_file_info64 *)fileInfo { + uLong permissionsMask = S_IRWXU | S_IRWXG | S_IRWXO; + return permissionsMask & (fileInfo->external_fa >> 16); +} + ++ (BOOL)itemIsDOSDirectory:(unz_file_info64 *)fileInfo { + return 0x01 & (fileInfo->external_fa >> 4); +} + ++ (BOOL)itemIsDirectory:(unz_file_info64 *)fileInfo { + UZKZipOS itemOS = [UZKFileInfo itemOS:fileInfo]; + if (itemOS == UZKZipOSMSDOS || itemOS == UZKZipOSWindowsNT) { + return [UZKFileInfo itemIsDOSDirectory:fileInfo]; + } + + return [UZKFileInfo itemFileType:fileInfo] == UZKFileTypeDirectory; +} + ++ (BOOL)itemIsSymbolicLink:(unz_file_info64 *)fileInfo { + return [UZKFileInfo itemFileType:fileInfo] == UZKFileTypeSymLink; +} + + + #pragma mark - Private Methods diff --git a/Tests/DeleteFileTests.m b/Tests/DeleteFileTests.m index 61a3d04..71db954 100644 --- a/Tests/DeleteFileTests.m +++ b/Tests/DeleteFileTests.m @@ -108,11 +108,11 @@ - (void)testDeleteFile_ThirdFile NSArray *testArchives = @[@"Test Archive.zip", @"Test Archive (Password).zip"]; - NSSet *expectedFileSet = self.nonZipTestFilePaths; - NSArray *expectedFiles = [expectedFileSet.allObjects sortedArrayUsingSelector:@selector(compare:)]; + NSSet *expectedFileSet = self.nonZipTestFilePaths; + NSArray *expectedFiles = [expectedFileSet.allObjects sortedArrayUsingSelector:@selector(compare:)]; NSString *fileToDelete = expectedFiles[2]; - NSMutableArray *newFileList = [NSMutableArray arrayWithArray:expectedFiles]; + NSMutableArray *newFileList = [NSMutableArray arrayWithArray:expectedFiles]; [newFileList removeObject:fileToDelete]; for (NSString *testArchiveName in testArchives) { diff --git a/Tests/FileInfoTests.swift b/Tests/FileInfoTests.swift new file mode 100644 index 0000000..2ff5324 --- /dev/null +++ b/Tests/FileInfoTests.swift @@ -0,0 +1,143 @@ +// +// FileInfoTests.swift +// UnzipKitTests +// +// Created by Dov Frankel on 7/25/19. +// Copyright © 2019 Abbey Code. All rights reserved. +// + +import XCTest + +class FileInfoTests: UZKArchiveTestCase { + + func testIsDirectory_NoDirectories() { + let testArchiveName = "Test Archive.zip" + let testFileURL = self.testFileURLs[testArchiveName] as! URL + let archive = try! UZKArchive(url: testFileURL) + + let fileInfo = try! archive.listFileInfo() + + let expected = [false, false, false] + let actual = fileInfo.map { $0.isDirectory } + + XCTAssertEqual(actual, expected) + } + + func testIsDirectory_ContainsDirectories() { + let testArchiveName = "Test Archive (Directories).zip" + let testFileURL = self.testFileURLs[testArchiveName] as! URL + let archive = try! UZKArchive(url: testFileURL) + + let fileInfo = try! archive.listFileInfo() + + let expected = [ + "Folder A": true, + "Folder A/Test File A.txt": false, + "Test File B.txt": false, + "X Folder": true, + "X Folder/Test File C.txt": false + ] + let actual = fileInfo.reduce(into: Dictionary()) { + $0[$1.filename] = $1.isDirectory + } + + XCTAssertEqual(actual, expected) + } + + func testIsDirectory_ContainsDirectories_DOS() { + let testArchiveName = "Test Archive (DOS Directories).zip" + let testFileURL = self.testFileURLs[testArchiveName] as! URL + let archive = try! UZKArchive(url: testFileURL) + + let fileInfo = try! archive.listFileInfo() + + let expected = [ + "FOLDERA": true, + "FOLDERA/TESTFILE.TXT": false, + "TESTFILE.TXT": false, + "XFOLDER": true, + "XFOLDER/TESTFILE.TXT": false + ] + let actual = fileInfo.reduce(into: Dictionary()) { + $0[$1.filename] = $1.isDirectory + } + + XCTAssertEqual(actual, expected) + } + + func testIsSymbolicLink_NoSymLinks() { + let testArchiveName = "Test Archive.zip" + let testFileURL = self.testFileURLs[testArchiveName] as! URL + let archive = try! UZKArchive(url: testFileURL) + + let fileInfo = try! archive.listFileInfo() + + let expected = [false, false, false] + let actual = fileInfo.map { $0.isSymbolicLink } + + XCTAssertEqual(actual, expected) + } + + #if os(OSX) + func testIsSymbolicLink_ContainsSymLinkFile() { + let textFileURL = self.emptyTextFile(ofLength: 20)! + let symLinkURL = textFileURL.deletingLastPathComponent() + .appendingPathComponent(textFileURL + .lastPathComponent + .replacingOccurrences(of: ".txt", with: "-Link.txt")) + + try! FileManager.default.createSymbolicLink(at: symLinkURL, withDestinationURL: textFileURL) + + let archiveURL = self.archive(withFiles: [textFileURL, symLinkURL], + zipOptions: ["--junk-paths", "--symlinks"])! + let archive = try! UZKArchive(url: archiveURL) + + let fileInfo = try! archive.listFileInfo() + + let expected = [ + textFileURL.lastPathComponent: false, + symLinkURL.lastPathComponent: true, + ] + let actual = fileInfo.reduce(into: Dictionary()) { + $0[$1.filename] = $1.isSymbolicLink + } + + XCTAssertEqual(actual, expected) + } + + func testIsDirectory_ContainsSymLinkFileAndDir() { + struct FileInfo: Equatable, CustomStringConvertible { + var description: String { + return "FileInfo(isLink: \(isLink), isDir: \(isDir))" + } + + let isLink: Bool + let isDir: Bool + + static func == (lhs: FileInfo, rhs: FileInfo) -> Bool { + return lhs.isLink == rhs.isLink && lhs.isLink == rhs.isLink + } + } + + let testArchiveName = "Test Archive (SymLink Directory).zip" + let testFileURL = self.testFileURLs[testArchiveName] as! URL + let archive = try! UZKArchive(url: testFileURL) + + let fileInfo = try! archive.listFileInfo() + + let expected: Dictionary = [ + "testDir": FileInfo(isLink: false, isDir: true), + "testDir/testFile2.md": FileInfo(isLink: false, isDir: false), + "testDirLink": FileInfo(isLink: true, isDir: false), + "testFile.md": FileInfo(isLink: false, isDir: false), + "testFileLink.md": FileInfo(isLink: true, isDir: false) + ] + let actual = fileInfo.reduce(into: Dictionary()) { + $0[$1.filename!] = FileInfo(isLink: $1.isSymbolicLink, isDir: $1.isDirectory) + } + + XCTAssertEqual(actual, expected) + } + #endif + +} diff --git a/Tests/ListFileInfoTests.m b/Tests/ListFileInfoTests.m index e3fcb7a..dd2f830 100644 --- a/Tests/ListFileInfoTests.m +++ b/Tests/ListFileInfoTests.m @@ -61,7 +61,7 @@ - (void)testListFileInfo { XCTAssertEqual(fileInfo.uncompressedSize, expectedFileSize, @"Incorrect uncompressed file size"); // Test Compression method - UZKCompressionMethod expectedCompressionMethod = ((NSNumber *)expectedCompressionMethods[fileInfo.filename]).integerValue; + UZKCompressionMethod expectedCompressionMethod = ((NSNumber *)expectedCompressionMethods[fileInfo.filename]).intValue; XCTAssertEqual(fileInfo.compressionMethod, expectedCompressionMethod, @"Incorrect compression method"); } } diff --git a/Tests/PerformOnFilesTests.m b/Tests/PerformOnFilesTests.m index d07da3c..c0908f4 100644 --- a/Tests/PerformOnFilesTests.m +++ b/Tests/PerformOnFilesTests.m @@ -21,7 +21,7 @@ - (void)testPerformOnFiles @"Test Archive (Password).zip"]; NSSet *expectedFileSet = self.nonZipTestFilePaths; - NSArray *expectedFiles = [expectedFileSet.allObjects sortedArrayUsingSelector:@selector(compare:)]; + NSArray *expectedFiles = [expectedFileSet.allObjects sortedArrayUsingSelector:@selector(compare:)]; for (NSString *testArchiveName in testArchives) { NSURL *testArchiveURL = self.testFileURLs[testArchiveName]; diff --git a/Tests/PermissionsTests.swift b/Tests/PermissionsTests.swift index 7910068..265d83b 100644 --- a/Tests/PermissionsTests.swift +++ b/Tests/PermissionsTests.swift @@ -93,7 +93,7 @@ class PermissionsTests: UZKArchiveTestCase { func testWriteData_Default() { let testArchiveURL = tempDirectory.appendingPathComponent("PermissionsTestWriteData.zip") - let testFilename = nonZipTestFilePaths.first as! String + let testFilename = nonZipTestFilePaths.first! let testFileURL = testFileURLs[testFilename] as! URL let testFileData = try! Data(contentsOf: testFileURL) @@ -112,7 +112,7 @@ class PermissionsTests: UZKArchiveTestCase { func testWriteData_NonDefault() { let testArchiveURL = tempDirectory.appendingPathComponent("PermissionsTestWriteData.zip") - let testFilename = nonZipTestFilePaths.first as! String + let testFilename = nonZipTestFilePaths.first! let testFileURL = testFileURLs[testFilename] as! URL let testFileData = try! Data(contentsOf: testFileURL) @@ -134,15 +134,16 @@ class PermissionsTests: UZKArchiveTestCase { func testWriteIntoBuffer_Default() { let testArchiveURL = tempDirectory.appendingPathComponent("PermissionsTestWriteBufferedData.zip") - let testFilename = nonZipTestFilePaths.first as! String + let testFilename = nonZipTestFilePaths.first! let testFileURL = testFileURLs[testFilename] as! URL let testFileData = try! Data(contentsOf: testFileURL) let writeArchive = try! UZKArchive(url: testArchiveURL) try! writeArchive.write(intoBuffer: testFilename) { (writeDataHandler, error) in - testFileData.withUnsafeBytes({ buffer in - writeDataHandler(buffer, UInt32(testFileData.count)) - }) + let buffer = testFileData.withUnsafeBytes { + $0.baseAddress?.assumingMemoryBound(to: UInt32.self) + } + return writeDataHandler(buffer!, UInt32(testFileData.count)) } let readArchive = try! UZKArchive(url: testArchiveURL) @@ -156,7 +157,7 @@ class PermissionsTests: UZKArchiveTestCase { func testWriteIntoBuffer_NonDefault() { let testArchiveURL = tempDirectory.appendingPathComponent("PermissionsTestWriteBufferedData_CustomPermissions.zip") - let testFilename = nonZipTestFilePaths.first as! String + let testFilename = nonZipTestFilePaths.first! let testFileURL = testFileURLs[testFilename] as! URL let testFileData = try! Data(contentsOf: testFileURL) @@ -166,9 +167,10 @@ class PermissionsTests: UZKArchiveTestCase { try! writeArchive.write(intoBuffer: testFilename, fileDate: nil, posixPermissions: expectedPermissions, compressionMethod: .default, overwrite: false, crc: 0, password: nil) { (writeDataHandler, error) in - testFileData.withUnsafeBytes({ buffer in - writeDataHandler(buffer, UInt32(testFileData.count)) - }) + let buffer = testFileData.withUnsafeBytes { + $0.baseAddress?.assumingMemoryBound(to: UInt32.self) + } + return writeDataHandler(buffer!, UInt32(testFileData.count)) } let readArchive = try! UZKArchive(url: testArchiveURL) diff --git a/Tests/ProgressReportingTests.m b/Tests/ProgressReportingTests.m index 7db7a28..87a0ba0 100644 --- a/Tests/ProgressReportingTests.m +++ b/Tests/ProgressReportingTests.m @@ -7,7 +7,7 @@ // #import "UZKArchiveTestCase.h" -#import "UnzipKit.h" +#import #import "UnzipKitMacros.h" @interface ProgressReportingTests : UZKArchiveTestCase @@ -87,6 +87,111 @@ - (void)testProgressReporting_ExtractFiles_FractionCompleted } } +#if !TARGET_OS_IPHONE +- (void)testProgressReporting_ExtractFiles_FractionCompleted_LargeFile +{ + NSURL *largeTextFile = [self emptyTextFileOfLength:1000000]; + NSURL *testArchiveURL = [self archiveWithFiles:@[largeTextFile]]; + + NSString *extractDirectory = [self randomDirectoryWithPrefix:@"LargeFileProgress"]; + NSURL *extractURL = [self.tempDirectory URLByAppendingPathComponent:extractDirectory]; + + UZKArchive *archive = [[UZKArchive alloc] initWithURL:testArchiveURL error:nil]; + + NSProgress *extractFilesProgress = [NSProgress progressWithTotalUnitCount:1]; + [extractFilesProgress becomeCurrentWithPendingUnitCount:1]; + + NSString *observedSelector = NSStringFromSelector(@selector(fractionCompleted)); + + [extractFilesProgress addObserver:self + forKeyPath:observedSelector + options:NSKeyValueObservingOptionInitial + context:ExtractFilesContext]; + + NSError *extractError = nil; + BOOL success = [archive extractFilesTo:extractURL.path + overwrite:NO + error:&extractError]; + + XCTAssertNil(extractError, @"Error returned by extractFilesTo:overwrite:progress:error:"); + XCTAssertTrue(success, @"Archive failed to extract large file to %@", extractURL); + + [extractFilesProgress resignCurrent]; + [extractFilesProgress removeObserver:self forKeyPath:observedSelector]; + + XCTAssertEqualWithAccuracy(extractFilesProgress.fractionCompleted, 1.00, .0000000001, @"Progress never reported as completed"); + + NSUInteger expectedProgressUpdates = 5; + NSArray *expectedProgresses = @[@0, + @0.262144, + @0.524287, + @0.786431, + @1.0]; + + XCTAssertEqual(self.fractionsCompletedReported.count, expectedProgressUpdates, @"Incorrect number of progress updates"); + for (NSUInteger i = 0; i < expectedProgressUpdates; i++) { + float expectedProgress = expectedProgresses[i].floatValue; + float actualProgress = self.fractionsCompletedReported[i].floatValue; + + XCTAssertEqualWithAccuracy(actualProgress, expectedProgress, 0.00001f, @"Incorrect progress reported at index %ld", (long)i); + } +} + +- (void)testProgressReporting_ExtractFiles_FractionCompleted_TwoLargeFiles +{ + NSArray *largeTextFiles = @[ + [self emptyTextFileOfLength:1000000], + [self emptyTextFileOfLength:500000], + ]; + NSURL *testArchiveURL = [self archiveWithFiles:largeTextFiles]; + + NSString *extractDirectory = [self randomDirectoryWithPrefix:@"TwoLargeFilesProgress"]; + NSURL *extractURL = [self.tempDirectory URLByAppendingPathComponent:extractDirectory]; + + UZKArchive *archive = [[UZKArchive alloc] initWithURL:testArchiveURL error:nil]; + + NSProgress *extractFilesProgress = [NSProgress progressWithTotalUnitCount:1]; + [extractFilesProgress becomeCurrentWithPendingUnitCount:1]; + + NSString *observedSelector = NSStringFromSelector(@selector(fractionCompleted)); + + [extractFilesProgress addObserver:self + forKeyPath:observedSelector + options:NSKeyValueObservingOptionInitial + context:ExtractFilesContext]; + + NSError *extractError = nil; + BOOL success = [archive extractFilesTo:extractURL.path + overwrite:NO + error:&extractError]; + + XCTAssertNil(extractError, @"Error returned by extractFilesTo:overwrite:progress:error:"); + XCTAssertTrue(success, @"Archive failed to extract large file to %@", extractURL); + + [extractFilesProgress resignCurrent]; + [extractFilesProgress removeObserver:self forKeyPath:observedSelector]; + + XCTAssertEqualWithAccuracy(extractFilesProgress.fractionCompleted, 1.00, .0000000001, @"Progress never reported as completed"); + + NSUInteger expectedProgressUpdates = 7; + NSArray *expectedProgresses = @[@0, + @0.174762, + @0.349525, + @0.524287, + @0.666667, + @0.841429, + @1.0]; + + XCTAssertEqual(self.fractionsCompletedReported.count, expectedProgressUpdates, @"Incorrect number of progress updates"); + for (NSUInteger i = 0; i < expectedProgressUpdates; i++) { + float expectedProgress = expectedProgresses[i].floatValue; + float actualProgress = self.fractionsCompletedReported[i].floatValue; + + XCTAssertEqualWithAccuracy(actualProgress, expectedProgress, 0.00001f, @"Incorrect progress reported at index %ld", (long)i); + } +} +#endif + - (void)testProgressReporting_ExtractFiles_Description { NSString *testArchiveName = @"Test Archive.zip"; @@ -568,10 +673,9 @@ - (void)observeValueForKeyPath:(NSString *)keyPath { observerCallCount++; - NSProgress *progress; + NSProgress *progress = object; - if ([object isKindOfClass:[NSProgress class]]) { - progress = object; + if ([progress isKindOfClass:[NSProgress class]]) { [self.fractionsCompletedReported addObject:@(progress.fractionCompleted)]; } else { return; diff --git a/Tests/Test Data/Test Archive (DOS Directories).zip b/Tests/Test Data/Test Archive (DOS Directories).zip new file mode 100644 index 0000000..99efd91 Binary files /dev/null and b/Tests/Test Data/Test Archive (DOS Directories).zip differ diff --git a/Tests/Test Data/Test Archive (Directories).zip b/Tests/Test Data/Test Archive (Directories).zip new file mode 100644 index 0000000..4ccadbc Binary files /dev/null and b/Tests/Test Data/Test Archive (Directories).zip differ diff --git a/Tests/Test Data/Test Archive (SymLink Directory).zip b/Tests/Test Data/Test Archive (SymLink Directory).zip new file mode 100644 index 0000000..14d535e Binary files /dev/null and b/Tests/Test Data/Test Archive (SymLink Directory).zip differ diff --git a/Tests/UZKArchiveTestCase.h b/Tests/UZKArchiveTestCase.h index 1373471..b85420f 100644 --- a/Tests/UZKArchiveTestCase.h +++ b/Tests/UZKArchiveTestCase.h @@ -23,9 +23,9 @@ @property (retain) NSURL *tempDirectory; @property (retain) NSMutableDictionary *testFileURLs; -@property (retain) NSMutableDictionary *unicodeFileURLs; -@property (retain) NSSet *nonZipTestFilePaths; -@property (retain) NSSet *nonZipUnicodeFilePaths; +@property (retain) NSMutableDictionary *unicodeFileURLs; +@property (retain) NSSet *nonZipTestFilePaths; +@property (retain) NSSet *nonZipUnicodeFilePaths; @property (retain) NSURL *corruptArchive; @@ -44,6 +44,9 @@ - (NSInteger)numberOfOpenFileHandles; - (NSURL *)archiveWithFiles:(NSArray *)fileURLs; - (NSURL *)archiveWithFiles:(NSArray *)fileURLs password:(NSString *)password; +- (NSURL *)archiveWithFiles:(NSArray *)fileURLs zipOptions:(NSArray *)zipOpts; +- (NSURL *)archiveWithFiles:(NSArray *)fileURLs password:(NSString *)password name:(NSString *)name; +- (NSURL *)archiveWithFiles:(NSArray *)fileURLs name:(NSString *)name zipOptions:(NSArray *)zipOpts; - (BOOL)extractArchive:(NSURL *)url password:(NSString *)password; - (NSURL *)largeArchive; #endif diff --git a/Tests/UZKArchiveTestCase.m b/Tests/UZKArchiveTestCase.m index 2cd9fe8..01c761b 100644 --- a/Tests/UZKArchiveTestCase.m +++ b/Tests/UZKArchiveTestCase.m @@ -52,7 +52,10 @@ - (void)setUp { NSArray *testFiles = @[ @"Test Archive.zip", + @"Test Archive (DOS Directories).zip", + @"Test Archive (Directories).zip", @"Test Archive (Password).zip", + @"Test Archive (SymLink Directory).zip", @"L'incertain.zip", @"Aces.zip", @"Comments Archive.zip", @@ -233,19 +236,36 @@ - (NSInteger)numberOfOpenFileHandles { return result; } -- (NSURL *)archiveWithFiles:(NSArray *)fileURLs +- (NSURL *)archiveWithFiles:(NSArray *)fileURLs { - return [self archiveWithFiles:fileURLs password:nil]; + return [self archiveWithFiles:fileURLs name:nil zipOptions:nil]; } -- (NSURL *)archiveWithFiles:(NSArray *)fileURLs password:(NSString *)password +- (NSURL *)archiveWithFiles:(NSArray *)fileURLs password:(NSString *)password { - NSString *uniqueString = [[NSProcessInfo processInfo] globallyUniqueString]; - return [self archiveWithFiles:fileURLs password:password name:uniqueString]; + return [self archiveWithFiles:fileURLs password:password name:nil]; } -- (NSURL *)archiveWithFiles:(NSArray *)fileURLs password:(NSString *)password name:(NSString *)name +- (NSURL *)archiveWithFiles:(NSArray *)fileURLs zipOptions:(NSArray *)zipOpts { + return [self archiveWithFiles:fileURLs name:nil zipOptions:zipOpts]; +} + +- (NSURL *)archiveWithFiles:(NSArray *)fileURLs password:(NSString *)password name:(NSString *)name +{ + return [self archiveWithFiles:fileURLs name:name zipOptions:@[@"--junk-paths", @"--password", password]]; +} + +- (NSURL *)archiveWithFiles:(NSArray *)fileURLs name:(NSString *)name zipOptions:(NSArray *)zipOpts; +{ + if (![name length]) { + name = [[NSProcessInfo processInfo] globallyUniqueString]; + } + + if (!zipOpts) { + zipOpts = @[@"--junk-paths"]; + } + NSURL *archiveURL = [[self.tempDirectory URLByAppendingPathComponent:name] URLByAppendingPathExtension:@"zip"]; NSFileHandle *consoleOutputHandle = nil; @@ -269,12 +289,8 @@ - (NSURL *)archiveWithFiles:(NSArray *)fileURLs password:(NSString *)password na while (startIndex < filePaths.count) { @autoreleasepool { - NSMutableArray *zipArgs = [NSMutableArray arrayWithArray: - @[@"-j", archiveURL.path]]; - - if (password) { - [zipArgs addObjectsFromArray:@[@"-P", password]]; - } + NSMutableArray *zipArgs = [NSMutableArray arrayWithArray:zipOpts]; + [zipArgs addObject:archiveURL.path]; NSRange currentRange = NSMakeRange(startIndex, MIN(pathsRemaining, maxFilesPerCall)); NSArray *pathArrayChunk = [filePaths subarrayWithRange:currentRange]; @@ -288,6 +304,7 @@ - (NSURL *)archiveWithFiles:(NSArray *)fileURLs password:(NSString *)password na UZKLog("Compressing files %lu-%lu of %lu", startIndex + 1, startIndex + pathArrayChunk.count, filePaths.count); + UZKLogDebug("Zip command: %@ %@", task.launchPath, [task.arguments componentsJoinedByString:@" "]); [task launch]; [task waitUntilExit]; @@ -345,8 +362,8 @@ - (NSURL *)largeArchive static NSInteger archiveNumber = 1; NSURL *largeArchiveURL = [self archiveWithFiles:emptyFiles - password:nil - name:[NSString stringWithFormat:@"Large Archive %ld", archiveNumber++]]; + name:[NSString stringWithFormat:@"Large Archive %ld", archiveNumber++] + zipOptions:nil]; return largeArchiveURL; } diff --git a/Tests/WriteBufferedDataTests.m b/Tests/WriteBufferedDataTests.m index bb541b0..7245866 100644 --- a/Tests/WriteBufferedDataTests.m +++ b/Tests/WriteBufferedDataTests.m @@ -18,11 +18,11 @@ @implementation WriteBufferedDataTests - (void)testWriteInfoBuffer { - NSArray *testFiles = [self.nonZipTestFilePaths.allObjects sortedArrayUsingSelector:@selector(compare:)]; - NSArray *testDates = @[[[UZKArchiveTestCase dateFormatter] dateFromString:@"12/20/2014 9:35 AM"], - [[UZKArchiveTestCase dateFormatter] dateFromString:@"12/21/2014 10:00 AM"], - [[UZKArchiveTestCase dateFormatter] dateFromString:@"12/22/2014 11:54 PM"]]; - NSMutableArray *testFileData = [NSMutableArray arrayWithCapacity:testFiles.count]; + NSArray *testFiles = [self.nonZipTestFilePaths.allObjects sortedArrayUsingSelector:@selector(compare:)]; + NSArray *testDates = @[[[UZKArchiveTestCase dateFormatter] dateFromString:@"12/20/2014 9:35 AM"], + [[UZKArchiveTestCase dateFormatter] dateFromString:@"12/21/2014 10:00 AM"], + [[UZKArchiveTestCase dateFormatter] dateFromString:@"12/22/2014 11:54 PM"]]; + NSMutableArray *testFileData = [NSMutableArray arrayWithCapacity:testFiles.count]; NSURL *testArchiveURL = [self.tempDirectory URLByAppendingPathComponent:@"WriteIntoBufferTest.zip"]; diff --git a/Tests/WriteDataTests.swift b/Tests/WriteDataTests.swift index c509ffa..2096b69 100644 --- a/Tests/WriteDataTests.swift +++ b/Tests/WriteDataTests.swift @@ -17,7 +17,7 @@ import UnzipKit class WriteDataTests: UZKArchiveTestCase { func testWriteData() { - let testFilePaths = [String](nonZipTestFilePaths as! Set).sorted(by: <) + let testFilePaths = [String](nonZipTestFilePaths).sorted(by: <) let testDates = [ UZKArchiveTestCase.dateFormatter().date(from: "12/20/2014 9:35 AM"), UZKArchiveTestCase.dateFormatter().date(from: "12/21/2014 10:00 AM"), @@ -57,7 +57,7 @@ class WriteDataTests: UZKArchiveTestCase { } func testWriteData_Unicode() { - let testFilePaths = [String](nonZipUnicodeFilePaths as! Set).sorted(by: <) + let testFilePaths = [String](nonZipUnicodeFilePaths).sorted(by: <) let testDates = [ UZKArchiveTestCase.dateFormatter().date(from: "12/20/2014 9:35 AM"), UZKArchiveTestCase.dateFormatter().date(from: "12/21/2014 10:00 AM"), @@ -97,7 +97,7 @@ class WriteDataTests: UZKArchiveTestCase { } func testWriteData_Overwrite() { - let testFilePaths = [String](nonZipTestFilePaths as! Set).sorted(by: <) + let testFilePaths = [String](nonZipTestFilePaths).sorted(by: <) let testDates = [ UZKArchiveTestCase.dateFormatter().date(from: "12/20/2014 9:35 AM"), UZKArchiveTestCase.dateFormatter().date(from: "12/21/2014 10:00 AM"), @@ -171,7 +171,7 @@ class WriteDataTests: UZKArchiveTestCase { } func testWriteData_Overwrite_Unicode() { - let testFilePaths = [String](nonZipUnicodeFilePaths as! Set).sorted(by: <) + let testFilePaths = [String](nonZipUnicodeFilePaths).sorted(by: <) let testDates = [ UZKArchiveTestCase.dateFormatter().date(from: "12/20/2014 9:35 AM"), UZKArchiveTestCase.dateFormatter().date(from: "12/21/2014 10:00 AM"), @@ -245,7 +245,7 @@ class WriteDataTests: UZKArchiveTestCase { } func testWriteData_NoOverwrite() { - let testFilePaths = [String](nonZipTestFilePaths as! Set).sorted(by: <) + let testFilePaths = [String](nonZipTestFilePaths).sorted(by: <) let testDates = [ UZKArchiveTestCase.dateFormatter().date(from: "12/20/2014 9:35 AM"), UZKArchiveTestCase.dateFormatter().date(from: "12/21/2014 10:00 AM"), @@ -305,7 +305,7 @@ class WriteDataTests: UZKArchiveTestCase { func testWriteData_MultipleWrites() { let testArchiveURL = tempDirectory.appendingPathComponent("MultipleDataWriteTest.zip") - let testFilename = nonZipTestFilePaths.first as! String + let testFilename = nonZipTestFilePaths.first! let testFileURL = testFileURLs[testFilename] as! URL let testFileData = try! Data(contentsOf: testFileURL) @@ -353,7 +353,7 @@ class WriteDataTests: UZKArchiveTestCase { func testWriteData_DefaultDate() { let testArchiveURL = tempDirectory.appendingPathComponent("DefaultDateWriteTest.zip") - let testFilename = nonZipTestFilePaths.first as! String + let testFilename = nonZipTestFilePaths.first! let testFileURL = testFileURLs[testFilename] as! URL let testFileData = try! Data(contentsOf: testFileURL) @@ -375,15 +375,52 @@ class WriteDataTests: UZKArchiveTestCase { XCTAssertEqual(actualDate, expectedDate, accuracy: 30, "Incorrect default date value written to file") } + func testWriteData_DeleteEmptyDirectory() { + let testArchiveURL = tempDirectory.appendingPathComponent("DeleteEmptyDir.zip") + let archive = try! UZKArchive(url: testArchiveURL) + + let testFilename = nonZipTestFilePaths.first as! String + let testFileURL = testFileURLs[testFilename] as! URL + let testFileData = try! Data(contentsOf:testFileURL) + try! archive.write(testFileData, filePath: testFilename) + + let emptyDirPath = "EmptyDirectory/" + try! archive.write(Data(), filePath: emptyDirPath) + + let initialFileList = try! archive.listFileInfo() + XCTAssertEqual(initialFileList.count, 2, "Expected a single directory") + + let trimmedEmptyDir = emptyDirPath.trimmingCharacters(in: CharacterSet(charactersIn: "/")) + + let initialLastFile = initialFileList.last! + XCTAssertEqual(initialLastFile.filename, trimmedEmptyDir, "Unexpected file path") + + try! archive.deleteFile(emptyDirPath) + + let fileListAfterDeleteDir = try! archive.listFileInfo() + XCTAssertEqual(fileListAfterDeleteDir.count, 1, "Expected no files to be listed after deleting empty directory") + + let lastFileAfterDeleteDir = fileListAfterDeleteDir.last! + XCTAssertEqual(lastFileAfterDeleteDir.filename, testFilename, "Unexpected file path") + + try! archive.write(Data(), filePath: emptyDirPath) + + let fileListAfterWriteDirAgain = try! archive.listFileInfo() + XCTAssertEqual(fileListAfterWriteDirAgain.count, 2, "Expected a single directory") + + let lastFileAfterWriteDirAgain = fileListAfterWriteDirAgain.last! + XCTAssertEqual(lastFileAfterWriteDirAgain.filename, trimmedEmptyDir, "Unexpected file path") + } + #if os(OSX) func testWriteData_PasswordProtected() { - let testFilePaths = [String](nonZipTestFilePaths as! Set).sorted(by: <) + let testFilePaths = [String](nonZipTestFilePaths).sorted(by: <) var testFileData = [Data]() let testArchiveURL = tempDirectory.appendingPathComponent("SwiftWriteDataTest.zip") let password = "111111" - let writeArchive = try! UZKArchive(path: testArchiveURL.path, password: password) + let writeArchive = try! UZKArchive(url: testArchiveURL, password: password) for testFilePath in testFilePaths { let fileData = try? Data(contentsOf: testFileURLs[testFilePath] as! URL) @@ -398,7 +435,7 @@ class WriteDataTests: UZKArchiveTestCase { // Read with UnzipKit - let readArchive = try! UZKArchive(path: testArchiveURL.path, password: password) + let readArchive = try! UZKArchive(url: testArchiveURL, password: password) XCTAssertTrue(readArchive.isPasswordProtected(), "Archive is not marked as password-protected") var index = 0 diff --git a/UnzipKit.xcodeproj/project.pbxproj b/UnzipKit.xcodeproj/project.pbxproj index 2986ac9..f4168f7 100644 --- a/UnzipKit.xcodeproj/project.pbxproj +++ b/UnzipKit.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 7A0029221F93DBC900618503 /* unzip.h in Headers */ = {isa = PBXBuildFile; fileRef = 96EA65C81A40C44300685B6D /* unzip.h */; }; 7A0029231F93DBC900618503 /* zip.h in Headers */ = {isa = PBXBuildFile; fileRef = 96EA65CA1A40C44300685B6D /* zip.h */; }; 7A0029241F93DBF000618503 /* libminizip.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A0029171F93DB5800618503 /* libminizip.a */; }; + 7A27405422EAA3A500E23E75 /* FileInfoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27405322EAA3A500E23E75 /* FileInfoTests.swift */; }; 7A5652241F90E01C006B782E /* CheckDataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A5652231F90E01C006B782E /* CheckDataTests.m */; }; 7A5A97011F89808900BCA061 /* ProgressReportingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A5A97001F89808900BCA061 /* ProgressReportingTests.m */; }; 7AA77FC822C16CF600121052 /* PermissionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA77FC722C16CF600121052 /* PermissionsTests.swift */; }; @@ -77,6 +78,7 @@ /* Begin PBXFileReference section */ 7A0029171F93DB5800618503 /* libminizip.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libminizip.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 7A27405322EAA3A500E23E75 /* FileInfoTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileInfoTests.swift; sourceTree = ""; }; 7A5652231F90E01C006B782E /* CheckDataTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CheckDataTests.m; sourceTree = ""; }; 7A5A97001F89808900BCA061 /* ProgressReportingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ProgressReportingTests.m; sourceTree = ""; }; 7AA77FC722C16CF600121052 /* PermissionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionsTests.swift; sourceTree = ""; }; @@ -252,6 +254,7 @@ 968C40C91B586227004C128E /* ExtractDataTests.m */, 9630C0371C6D27A4000693EE /* ExtractDataTests_Swift.swift */, 968C40C71B5861F4004C128E /* ExtractFilesTests.m */, + 7A27405322EAA3A500E23E75 /* FileInfoTests.swift */, 7A5A97001F89808900BCA061 /* ProgressReportingTests.m */, 968C40D91B5863D9004C128E /* FileDescriptorUsageTests.m */, 968C40C31B58619C004C128E /* ListFilenamesTests.m */, @@ -425,7 +428,7 @@ attributes = { LastSwiftMigration = 0700; LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0930; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = "Abbey Code"; TargetAttributes = { 7A0029161F93DB5800618503 = { @@ -441,13 +444,13 @@ }; 96EA65A81A40AEAE00685B6D = { CreatedOnToolsVersion = 6.1.1; - LastSwiftMigration = 0900; + LastSwiftMigration = 1020; }; }; }; buildConfigurationList = 96EA65981A40AEAE00685B6D /* Build configuration list for PBXProject "UnzipKit" */; compatibilityVersion = "Xcode 8.0"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -545,6 +548,7 @@ 968C40E01B586490004C128E /* PropertyTests.m in Sources */, 968C40DC1B586401004C128E /* CommentsTests.m in Sources */, 968C40D21B586310004C128E /* PasswordProtectionTests.m in Sources */, + 7A27405422EAA3A500E23E75 /* FileInfoTests.swift in Sources */, 963386B91EE89A51006B16BF /* UtilityMethods.swift in Sources */, 7AA77FC822C16CF600121052 /* PermissionsTests.swift in Sources */, 968C40C81B5861F4004C128E /* ExtractFilesTests.m in Sources */, @@ -675,6 +679,7 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -721,7 +726,7 @@ MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; WARNING_CFLAGS = ( "-Weverything", "-Wno-auto-import", @@ -735,6 +740,7 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -776,7 +782,7 @@ MTL_ENABLE_DEBUG_INFO = NO; SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; WARNING_CFLAGS = ( "-Weverything", "-Wno-auto-import", @@ -836,7 +842,6 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Tests/UnzipKitTests-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.0; WARNING_CFLAGS = ( "$(inherited)", "-Wno-gnu-statement-expression", @@ -846,6 +851,7 @@ "-Wno-pointer-arith", "-Wno-cast-qual", "-Wno-undef", + "-Wno-objc-messaging-id", ); }; name = Debug; @@ -860,7 +866,6 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.abbey-code.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Tests/UnzipKitTests-Bridging-Header.h"; - SWIFT_VERSION = 4.0; WARNING_CFLAGS = ( "$(inherited)", "-Wno-everything", diff --git a/UnzipKit.xcodeproj/xcshareddata/xcschemes/UnzipKit.xcscheme b/UnzipKit.xcodeproj/xcshareddata/xcschemes/UnzipKit.xcscheme index 7ead498..0697e6d 100644 --- a/UnzipKit.xcodeproj/xcshareddata/xcschemes/UnzipKit.xcscheme +++ b/UnzipKit.xcodeproj/xcshareddata/xcschemes/UnzipKit.xcscheme @@ -1,6 +1,6 @@ Bool { + internal func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } diff --git a/beta-notes.md b/beta-notes.md index e908b45..5b6bbcd 100644 --- a/beta-notes.md +++ b/beta-notes.md @@ -1 +1,3 @@ -Added support for archiving and restoring files' POSIX permissions (PRs #84, #86, #87 - Thanks, [@MartinLau7](https://github.com/MartinLau7)!) \ No newline at end of file +* Removed methods deprecated in v1.9 (Issue #90, PR #92) +* Fixed behavior of `-extractFilesTo:overwrite:error:`, so it shows the progress of each individual file as they extract (Issue #91, PR #94) +* Deprecated the initializers that take a file path instead of an `NSURL` (Issue #90, PR #95) \ No newline at end of file