Why is autolinking failing on react-native-zip-archive?

Viewed 287

I have an Expo project to which I'm trying to add react-native-zip-archive. I've followed the docs exactly, but autolinking isn’t working on Android (haven’t tried iOS). I get an error on the package index file, TypeError: null is not an object (evaluating 'RNZipArchive.unzip').

I suspect there’s something in the package gradle that's to blame, but I don't know enough to debug it. Anything look off here?

buildscript {
    repositories {
        jcenter()
        google()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
    }
}

apply plugin: 'com.android.library'

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion

    defaultConfig {
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode 1
        versionName "1.0"
    }
    lintOptions {
        abortOnError false
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation "com.facebook.react:react-native:+"
    implementation "net.lingala:zip4j:1.3.3"
}
1 Answers

I could not find a solution for null is not an object error in react-native-zip-archive either. So I ended up writing a Java module and linked it with my react-native project. It is a lot faster and easier to use.

Save this file as ZipModule.java in android/app/src/main/java/com/YOUR PROJECT/

package com.<YOUR PROJECT>;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import java.util.Map;
import java.util.HashMap;

import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;

import java.io.File;
import java.io.IOException;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import android.util.Log;

public class ZipModule extends ReactContextBaseJavaModule {
    ZipModule(ReactApplicationContext context) {
        super(context);
    }

    @Override
    public String getName() {
        return "ZipModule";
    }

    @ReactMethod
    public void unzip(String zipFile, String targetDirectory, Callback cb) {
        
        try {
            ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile)));

            ZipEntry ze;
            int count;
            
            byte[] buffer = new byte[8192];
            
            while ((ze = zis.getNextEntry()) != null) {
                File file = new File(targetDirectory, ze.getName());
                File dir = ze.isDirectory() ? file : file.getParentFile();
                if (!dir.isDirectory() && !dir.mkdirs()) {
                    throw new FileNotFoundException("Failed to ensure directory: " + dir.getAbsolutePath());
                }
                
                if (ze.isDirectory()) {
                    continue;
                }

                FileOutputStream fout = new FileOutputStream(file);
                try {
                    while ((count = zis.read(buffer)) != -1) {
                        fout.write(buffer, 0, count);
                    }
                } finally {
                    fout.close();
                }
            }
            zis.close();
        }
        catch(FileNotFoundException err) {
            cb.invoke(err);
        }
        catch(Exception err) {
            cb.invoke(err);
        }
        finally {
            cb.invoke();
        }
    }
} 

Save this file as MyAppPackage.java in android/app/src/main/java/com/YOUR PROJECT/

package com.<YOUR PROJECT>;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class MyAppPackage implements ReactPackage {

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }

    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();

        modules.add(new ZipModule(reactContext));

        return modules;
    }

}

And in your MainApplication.java in android/app/src/main/java/com/YOUR PROJECT/, add your MyAppPackage class

@Override
protected List<ReactPackage> getPackages() {
   @SuppressWarnings("UnnecessaryLocalVariable")
   List<ReactPackage> packages = new PackageList(this).getPackages();
   // Packages that cannot be autolinked yet can be added manually here
   packages.add(new MyAppPackage());
   return packages;
}

And that's it; your native zip extraction module is ready to use. To call it in your JS code, try the following example

import { NativeModules } from "react-native";

/* Note: do not destructure ZipModule from NativeModules. It doesn't work that way */
const ZipModule = NativeModules.ZipModule;

ZipModule.unzip(zipFile, targetDir, (err) => {
   if(err) {
   /* Handle the errors here */
      console.error(err);
      return;
   }
   /* Handle What you want to do after file extraction */
}) 

Also remember to rebuild your app (react-native run-android) everytime you make changes to the Java class

Related