Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions .github/workflows/old-arch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
name: Old Architecture
# RNZA-5: prove v9 still builds when New Architecture is off.
# playground-rn is RN 0.83 — since 0.82, newArchEnabled=false / RCT_NEW_ARCH_ENABLED=0
# are ignored. Last opt-out line is RN 0.81.

on:
pull_request:
branches: [master]
push:
branches: [master]
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
RN_OLD_ARCH_VERSION: '0.81.6'
RN_OLD_ARCH_CLI: '15.1.3'

jobs:
compile-android-0-81:
name: Compile Android old-arch (RN 0.81)
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Checkout
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0

- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 20

- name: Setup Java
uses: actions/setup-java@cf277c60eb25467037889841efdb72551f06f6c3 # v4.9.1
with:
distribution: 'zulu'
java-version: '17'

- name: Pack library
run: |
set -euo pipefail
TARBALL=$(npm pack --silent)
echo "TARBALL=$TARBALL" >> "$GITHUB_ENV"
echo "Packed $TARBALL"

- name: Init RN ${{ env.RN_OLD_ARCH_VERSION }} app
working-directory: ${{ runner.temp }}
env:
CI: '1'
run: |
set -euo pipefail
npx --yes "@react-native-community/cli@${RN_OLD_ARCH_CLI}" init RnzaOldArch \
--version "$RN_OLD_ARCH_VERSION" \
--pm npm \
--skip-git-init \
--install-pods=false
test -d RnzaOldArch

- name: Install packed library
working-directory: ${{ runner.temp }}/RnzaOldArch
run: |
set -euo pipefail
npm install "${GITHUB_WORKSPACE}/${TARBALL}"

- name: Disable New Architecture
working-directory: ${{ runner.temp }}/RnzaOldArch/android
run: |
set -euo pipefail
grep -q '^newArchEnabled=' gradle.properties
sed -i 's/^newArchEnabled=.*/newArchEnabled=false/' gradle.properties
grep '^newArchEnabled=false$' gradle.properties

- name: Assemble release
working-directory: ${{ runner.temp }}/RnzaOldArch/android
run: ./gradlew :app:assembleRelease --no-configuration-cache

- name: Assert New Architecture is off
working-directory: ${{ runner.temp }}/RnzaOldArch/android
run: |
set -euo pipefail
app_bc=$(find app/build/generated -name BuildConfig.java | head -1)
test -n "$app_bc"
echo "App BuildConfig: $app_bc"
grep -n 'IS_NEW_ARCHITECTURE_ENABLED' "$app_bc"
grep -E 'IS_NEW_ARCHITECTURE_ENABLED = (false|Boolean\.parseBoolean\("false"\))' "$app_bc"
echo "Library BuildConfig:"
lib_hit=$(find . "${GITHUB_WORKSPACE}/android" -name BuildConfig.java 2>/dev/null \
| xargs grep -l 'package com.rnziparchive;' 2>/dev/null || true)
echo "$lib_hit"
if [ -n "$lib_hit" ]; then
echo "$lib_hit" | xargs grep -n 'IS_NEW_ARCHITECTURE_ENABLED'
echo "$lib_hit" | xargs grep -E 'IS_NEW_ARCHITECTURE_ENABLED = (false|Boolean\.parseBoolean\("false"\))'
fi

compile-ios-0-81:
name: Compile iOS old-arch (RN 0.81)
runs-on: macos-15
timeout-minutes: 45
steps:
- name: Checkout
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0

- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 20

- name: Pack library
run: |
set -euo pipefail
TARBALL=$(npm pack --silent)
echo "TARBALL=$TARBALL" >> "$GITHUB_ENV"
echo "Packed $TARBALL"

- name: Init RN ${{ env.RN_OLD_ARCH_VERSION }} app
working-directory: ${{ runner.temp }}
env:
CI: '1'
run: |
set -euo pipefail
npx --yes "@react-native-community/cli@${RN_OLD_ARCH_CLI}" init RnzaOldArch \
--version "$RN_OLD_ARCH_VERSION" \
--pm npm \
--skip-git-init \
--install-pods=false
test -d RnzaOldArch

- name: Install packed library
working-directory: ${{ runner.temp }}/RnzaOldArch
run: |
set -euo pipefail
npm install "${GITHUB_WORKSPACE}/${TARBALL}"

- name: Install CocoaPods (RCT_NEW_ARCH_ENABLED=0)
working-directory: ${{ runner.temp }}/RnzaOldArch/ios
run: |
set -euo pipefail
# RN 0.81 defaults to iOS 15.1; this library requires 15.5 (App Store privacy).
sed -i '' "s/platform :ios, min_ios_version_supported/platform :ios, '15.5'/" Podfile
grep "platform :ios" Podfile
gem install cocoapods -v '>= 1.13'
RCT_NEW_ARCH_ENABLED=0 pod install

- name: Build iOS app
working-directory: ${{ runner.temp }}/RnzaOldArch/ios
env:
RCT_NEW_ARCH_ENABLED: '0'
run: |
set -euo pipefail
WORKSPACE=$(ls -d *.xcworkspace)
SCHEME="${WORKSPACE%.xcworkspace}"
echo "Using workspace=$WORKSPACE scheme=$SCHEME"
xcodebuild \
-workspace "$WORKSPACE" \
-scheme "$SCHEME" \
-configuration Release \
-destination 'generic/platform=iOS Simulator' \
-derivedDataPath build \
CODE_SIGNING_ALLOWED=NO \
COMPILER_INDEX_STORE_ENABLE=NO
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

## [Unreleased]

### Fixed
- Android: register as a legacy Native Module when New Architecture is off (`isTurboModule` follows `BuildConfig.IS_NEW_ARCHITECTURE_ENABLED`) so `NativeModules.RNZipArchive` is non-null on old-arch apps (RNZA-5)

### Added
- CI: RN 0.81.6 old-architecture Android + iOS compile in `.github/workflows/old-arch.yml` (RNZA-5). RN 0.82+ cannot opt out of New Architecture.

### Changed
- README + MIGRATION: install table by RN version — v7 only for RN < 0.70; v9 on 0.70–0.81 including old architecture; New Architecture only on 0.82+

## [9.5.0] - 2026-09-04

### Added
Expand Down
24 changes: 16 additions & 8 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ The JavaScript API is unchanged from v7 through v9 — no call-site changes. You

Working examples: [playground-expo](./playground-expo/) and [playground-rn](./playground-rn/).

Stay on v7 only for React Native **< 0.70**. On 0.70+, New Architecture is recommended. If the native module fails to load on an old-architecture 0.70+ app, fall back to v7 until Interop is confirmed.
| Your React Native | What to install |
|-------------------|-----------------|
| **< 0.70** | Stay on `^7.0.0` (or upgrade RN first) |
| **0.70–0.81** | Latest v9. New Architecture recommended; old architecture works. Rebuild native. |
| **0.82+** | Latest v9. New Architecture only — RN ignores `newArchEnabled=false` / `RCT_NEW_ARCH_ENABLED=0`. |

Old-arch proof is compile + link on RN **0.81.6** (`.github/workflows/old-arch.yml`), not device Maestro. See the [README matrix](./README.md#old-architecture-rn-070081).

## v9.5

Expand Down Expand Up @@ -133,7 +139,7 @@ v8.0 migrates `react-native-zip-archive` from Legacy Native Modules to **TurboMo

**JavaScript API is unchanged.** No JavaScript call-site changes are required.

Use v8+/v9 on React Native >= 0.70. Stay on v7 only if you are on React Native **< 0.70**. New Architecture is recommended; old-architecture Interop on 0.70+ is not confirmed — if the native module fails to load, fall back to v7 or enable New Architecture (see Troubleshooting).
Use v8+/v9 on React Native >= 0.70. Stay on v7 only if you are on React Native **< 0.70**. New Architecture is recommended, not required on 0.70–0.81. Do not add a separate Interop package. RN 0.82+ is New Architecture only — see the [README matrix](./README.md#old-architecture-rn-070081).

### Migration Steps

Expand All @@ -145,16 +151,18 @@ npx react-native --version

If you're on React Native < 0.70, stay on v7.x of this library (or upgrade React Native to 0.70+ first).

#### Step 2: New Architecture (recommended)
#### Step 2: New Architecture (recommended, not required on 0.70–0.81)

This library does **not** require New Architecture on RN 0.70–0.81. On RN 0.82+ you cannot turn it off ([RN 0.82](https://reactnative.dev/blog/2025/10/08/react-native-0.82)).

Follow the [official React Native guide](https://reactnative.dev/docs/new-architecture-intro).
If you want New Architecture on 0.70–0.81, follow the [official React Native guide](https://reactnative.dev/docs/new-architecture-intro):

**Android**: In `android/gradle.properties`:
```properties
newArchEnabled=true
```

**iOS**: Reinstall pods with New Architecture enabled:
**iOS**:
```bash
cd ios
RCT_NEW_ARCH_ENABLED=1 pod install
Expand Down Expand Up @@ -200,10 +208,10 @@ npm install react-native-zip-archive@^7.0.0

| Issue | Solution |
|-------|----------|
| "Native module not found" | Enable New Architecture first. On an old-architecture 0.70+ app, fall back to `^7.0.0` until Interop is confirmed. |
| Build fails on iOS | Delete `ios/Pods` and `ios/Podfile.lock`, then `pod install` |
| "Native module not found" | Rebuild the native app (`pod install` + Android rebuild). Stay on `^7.0.0` only for RN **< 0.70**. On 0.70–0.81 with New Architecture off, v9 loads via `NativeModules` after a native rebuild (see [README](./README.md#old-architecture-rn-070081)). If it is still null after rebuild, open an issue with RN version and `newArchEnabled`. |
| Build fails on iOS | Delete `ios/Pods` and `ios/Podfile.lock`, then `pod install`. This library requires iOS **15.5+**. |
| Build fails on Android | Run `./gradlew clean` and clear Metro cache |
| Works on Android but not iOS | Ensure you ran `RCT_NEW_ARCH_ENABLED=1 pod install` |
| Works on Android but not iOS | Confirm a native rebuild. On RN 0.82+ New Architecture cannot be disabled. On 0.70–0.81, `RCT_NEW_ARCH_ENABLED=1` is optional for this library. |
| Expo Go shows "Native module not found" | Use Expo Development Build instead |

### Need Help?
Expand Down
37 changes: 31 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

Zip archive utility for React Native.

> Latest **v8+ / v9** targets React Native ≥ 0.70 with TurboModules. **New Architecture is recommended.**
> **v9** is for React Native ≥ 0.70. New Architecture is recommended. Stay on v7 only for RN **< 0.70**.
>
> Use `^7.0.0` only if you are on React Native **< 0.70**:
> ```bash
> npm install react-native-zip-archive@^7.0.0
> ```
> If the native module fails to load on an old-architecture 0.70+ app, fall back to v7 until Interop Layer support is confirmed.
> | Your React Native | Install |
> |-------------------|---------|
> | **< 0.70** | `npm install react-native-zip-archive@^7.0.0` |
> | **0.70–0.81** | latest v9 (old architecture works; native rebuild required) |
> | **0.82+** | latest v9 (New Architecture only — RN ignores the opt-out flags) |
>
> **iOS:** Version 7.0.0+ requires a deployment target of iOS 15.5+ to comply with App Store privacy policy.

Expand All @@ -21,6 +21,31 @@ Zip archive utility for React Native.
| iOS | >= 15.5 |
| Android | >= API 23 (Android 6.0) |

## Old architecture (RN 0.70–0.81)

Do not stay on v7 for old architecture on RN 0.70+. Install latest v9 and rebuild native.

| Surface | How v9 loads when New Architecture is off |
|---------|-------------------------------------------|
| JS | `TurboModuleRegistry.get('RNZipArchive')`, then `NativeModules.RNZipArchive` |
| Android | `isTurboModule` follows `BuildConfig.IS_NEW_ARCHITECTURE_ENABLED`; paper specs compile when new arch is off |
| iOS | `RCT_EXPORT_MODULE` always; `getTurboModule` is `#ifdef RCT_NEW_ARCH_ENABLED` |

| RN | Android `newArchEnabled=false` | iOS `RCT_NEW_ARCH_ENABLED=0` | Evidence |
|----|--------------------------------|------------------------------|----------|
| **0.82+** ([playground-rn](./playground-rn/) 0.83.9) | N/A — flag ignored | N/A — flag ignored | [RN 0.82](https://reactnative.dev/blog/2025/10/08/react-native-0.82); zip/unzip Maestro on New Arch (`e2e.yml`) |
| **0.81.6** (last opt-out) | compile + `IS_NEW_ARCHITECTURE_ENABLED=false` | compile + Legacy Architecture | `.github/workflows/old-arch.yml` (not device Maestro) |
| **0.73–0.80** | same native paths as 0.81 | same | inferred; not separately built |

Reproduce the 0.81 compile (same as CI):

```bash
npx @react-native-community/cli@15.1.3 init RnzaOldArch --version 0.81.6 --pm npm --skip-git-init
cd RnzaOldArch && npm install /path/to/react-native-zip-archive
# Android: set newArchEnabled=false in android/gradle.properties, then assembleRelease
# iOS: set platform :ios, '15.5' in the Podfile, then RCT_NEW_ARCH_ENABLED=0 pod install
```

## Comparison

| | This library | JSZip in React Native | Nitro (`react-native-nitro-unzip` / `react-native-nitro-archive`) |
Expand Down
16 changes: 13 additions & 3 deletions __tests__/package-metadata.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,20 @@ describe('docs claims vs native source (RNZA-7/15/17/19)', () => {
expect(ios).not.toMatch(/SSZipArchive unzipFileAtPath/);
});

test('README does not claim old-arch Interop is proven', () => {
test('README documents old-arch v9 load path and playground-rn CI', () => {
const readme = read('README.md');
expect(readme).toMatch(/old-architecture 0\.70\+ app/);
expect(readme).not.toMatch(/old architecture is (fully )?supported/i);
const migration = read('MIGRATION.md');
const pkgJava = read('android/src/main/java/com/rnziparchive/RNZipArchivePackage.java');
expect(readme).toMatch(/old-arch\.yml/);
expect(readme).toMatch(/newArchEnabled=false/);
expect(readme).toMatch(/RCT_NEW_ARCH_ENABLED=0/);
expect(readme).toMatch(/Stay on v7 only for RN/);
expect(readme).toMatch(/0\.70–0\.81/);
expect(readme).toMatch(/0\.82\+/);
expect(migration).toMatch(/0\.70–0\.81/);
expect(migration).toMatch(/recommended, not required on 0\.70–0\.81/);
expect(pkgJava).toMatch(/boolean isTurboModule = BuildConfig\.IS_NEW_ARCHITECTURE_ENABLED/);
expect(pkgJava).not.toMatch(/true\s*\/\/ isTurboModule = true/);
});

test('README records AbortSignal and ZipError usage', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,18 @@ public NativeModule getModule(String name, ReactApplicationContext reactContext)
public ReactModuleInfoProvider getReactModuleInfoProvider() {
return () -> {
Map<String, ReactModuleInfo> map = new HashMap<>();
// Follow the host app flag. Hardcoding isTurboModule=true hides this
// module from the legacy NativeModule registry when newArchEnabled=false,
// so NativeModules.RNZipArchive is null and JS cannot fall back.
boolean isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
map.put(RNZipArchiveModule.NAME, new ReactModuleInfo(
RNZipArchiveModule.NAME, // name
RNZipArchiveModule.NAME, // className
false, // canOverrideExistingModule
false, // needsEagerInit
true, // hasConstants — required for RN 0.70.x compat
false, // isCXXModule
true // isTurboModule = true
isTurboModule
));
return map;
};
Expand Down
6 changes: 6 additions & 0 deletions playground-rn/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ This is a new [**React Native**](https://reactnative.dev) project, bootstrapped

> **Note**: Make sure you have completed the [Set Up Your Environment](https://reactnative.dev/docs/set-up-your-environment) guide before proceeding.

## Old architecture (RNZA-5)

This app is **RN 0.83.9**. Since React Native 0.82, `newArchEnabled=false` and `RCT_NEW_ARCH_ENABLED=0` are ignored — New Architecture is the only option.

Old-architecture compile proof lives in `.github/workflows/old-arch.yml` against **RN 0.81.6** (last version that can opt out).

## Step 1: Start Metro

First, you will need to run **Metro**, the JavaScript build tool for React Native.
Expand Down
Loading