Skip to content

fix(ios): add directory array items recursively in zip/zipWithPassword (#339)#362

Merged
plrthink merged 1 commit into
masterfrom
fix/zip-array-directory-consistency
Jul 22, 2026
Merged

fix(ios): add directory array items recursively in zip/zipWithPassword (#339)#362
plrthink merged 1 commit into
masterfrom
fix/zip-array-directory-consistency

Conversation

@plrthink

@plrthink plrthink commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Fixes #339.

Problem

zip(string[]) behaved inconsistently across platforms when an array item is a directory:

  • Android adds the directory's contents recursively, with entry names relative to the listed directory (a.txt, sub/b.txt).
  • iOS passed the array straight to SSZipArchive's createZipFileAtPath:withFilesAtPaths:, which writes a directory as an empty entry — contents were silently dropped.

Fix

iOS now expands the array itself before writing, using SSZipArchive's public instance API (initWithPath:/open/writeFileAtPath:withFileName:.../close`):

  • file item → entry name is its base name (unchanged)
  • directory item → each contained file is written with its path relative to the listed directory, matching Android's entry names

Applied to both zipFiles and zipFilesWithPassword.

Preserved behaviors

  • Per-entry progress events (total = expanded entry count)
  • zipFilesWithPassword still writes with AES:YES, matching the previous withFilesAtPaths: code path
  • compressionLevel remains ignored for file arrays on iOS (pre-existing; already documented)

Residual divergence

Android preserves empty directories (zip4j folder entries); iOS skips directory entries. Documented in the README along with the aligned behavior.

Verification

  • playground-rn iOS app builds successfully (xcodebuild, Debug / iOS Simulator)
  • npx jest — 27/27 pass

Open in Devin Review

#339)

Passing a directory in a zip(string[]) array produced an empty entry on
iOS (SSZipArchive withFilesAtPaths: writes directories as empty entries),
while Android adds the directory's contents recursively. iOS now expands
directory items itself and writes each contained file with its path
relative to the listed directory via SSZipArchive's instance API,
matching Android's entry names and behavior.

Existing behaviors are preserved: per-entry progress events, AES:YES
writes for zipWithPassword (as before via withFilesAtPaths:), and
compressionLevel remaining ignored for file arrays on iOS. One residual
divergence: empty directories are preserved on Android only; documented
in the README.
@cursor

cursor Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

Open in Devin Review

Comment thread ios/RNZipArchive.mm
Comment on lines +202 to +208
NSUInteger total = entries.count, complete = 0;
for (NSArray<NSString *> *entry in entries) {
success &= [zipArchive writeFileAtPath:entry[0] withFileName:entry[1] compressionLevel:compressionLevel password:password AES:aes];
if (self.progressHandler) {
complete++;
self.progressHandler(complete, total);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Progress event denominator differs from Android for directory inputs

The new iOS writeZipEntriesToPath precomputes total = entries.count where entries are the fully recursively-expanded files (ios/RNZipArchive.mm:202). Android's processZip instead accumulates totalFiles incrementally and, for a directory item, counts only the immediate children (android/.../RNZipArchiveModule.java:381-393), calling addFolder once per immediate subdirectory while still incrementing the counter by 1. This means for directory inputs the two platforms emit a different number of intermediate progress ticks and different intermediate progress fractions. REVIEW.md states the progress contract was aligned in v9 and that divergence should be treated as a bug. In practice the iOS side is arguably more correct (monotonic, precomputed denominator), while Android's incremental denominator can jump around, so I did not flag this as a hard bug — but reviewers relying on strict cross-platform progress parity should be aware the tick counts diverge for directory array items.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@plrthink

Copy link
Copy Markdown
Collaborator Author

Re: Devin's comment on the progress denominator — acknowledged, and we're leaving it as-is in this PR on purpose. The iOS side (precomputed, monotonic denominator over fully expanded entries) is the more correct behavior and closer to the v9 progress contract; Android's processZip counting (incremental denominator, immediate-children-only for directory items) is the weaker side. Aligning Android's zip progress accounting is a separate piece of work and out of scope for this fix, which targets the content-level divergence from #339 (silently dropped directory contents).

@plrthink
plrthink merged commit fed84fc into master Jul 22, 2026
12 of 13 checks passed
@plrthink
plrthink deleted the fix/zip-array-directory-consistency branch July 22, 2026 01:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Behavior of zip(string[]) is inconsistent across platforms

1 participant