npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

rojma-app

v2.0.0

Published

Android app update checker, APK downloader, and APK installer for Expo and React Native CLI apps

Readme

rojma-app-update

Android app update checker, APK downloader, and installer for Expo and React Native CLI apps.

Features

Expo + React Native CLI Support - Works seamlessly in both environments
Auto-Detection - Automatically detects your platform, no configuration needed
Update Checking - Check your backend API for newer app versions
APK Download - Download APK files with real-time progress (0-1)
APK Installation - Launch Android APK installer
Mandatory & Optional Updates - Support both update types
Error Handling - Typed error classes for different scenarios
TypeScript - Full TypeScript support with strong typing
Zero Hardcoding - Everything is configurable

Installation

Expo Projects

npm install rojma-app-update
npx expo install expo-file-system expo-intent-launcher expo-linking

React Native CLI Projects

npm install rojma-app-update react-native-fs

Add to android/app/src/main/AndroidManifest.xml:

<manifest>
  <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
  <!-- ... other permissions ... -->
</manifest>

Optional (for better APK installation):

npm install react-native-send-intent

Quick Start

Expo Example

import { useAppUpdate } from "rojma-app-update";
import Constants from "expo-constants";

function App() {
  const { updateState, updateNow, handleLater, downloadProgress } = useAppUpdate({
    apiUrl: "https://api.example.com/check-version",
    version: Constants.expoConfig?.version ?? "1.0.0",
    buildType: "production",
    projectName: "MyApp",
    packageName: Constants.expoConfig?.android?.package ?? "",
  });

  if (updateState === "available") {
    return (
      <View>
        <Text>Update Available!</Text>
        <Button title="Update Now" onPress={updateNow} />
        <Button title="Later" onPress={handleLater} />
      </View>
    );
  }

  return <YourApp />;
}

React Native CLI Example

import { useAppUpdate } from "rojma-app-update";
import { version } from "../package.json";

function App() {
  const { updateState, updateNow, handleLater, downloadProgress } = useAppUpdate({
    apiUrl: "https://api.example.com/check-version",
    version: version,
    buildType: __DEV__ ? "debug" : "production",
    projectName: "MyApp",
    packageName: "com.myapp",
  });

  if (updateState === "available") {
    return (
      <View>
        <Text>Update Available!</Text>
        <Button title="Update Now" onPress={updateNow} />
        <Button title="Later" onPress={handleLater} />
      </View>
    );
  }

  return <YourApp />;
}

See example-usage.tsx for a complete example with a modal UI.

API

useAppUpdate(config)

const {
  updateState,        // "idle" | "checking" | "hidden" | "available" | "downloading" | "error"
  latestVersion,      // string | null
  currentVersion,     // string
  isMandatory,        // boolean
  errorMessage,       // string
  error,             // Error | null
  response,          // AppUpdateResponse | null
  checkForUpdate,    // () => Promise<void>
  updateNow,         // () => Promise<void>
  openWebsite,       // () => Promise<void>
  handleLater,       // () => void
  retry,            // () => Promise<void>
  downloading,      // boolean
  downloadProgress, // number | null (0-1)
} = useAppUpdate(config);

Configuration

interface AppUpdateConfig {
  apiUrl: string;                 // Backend endpoint (POST)
  version: string;                // Current app version
  buildType: string;              // "production", "staging", etc.
  projectName: string;            // Project name for backend
  packageName: string;            // Android package name
  websiteUrl?: string;            // Fallback website URL
  cacheFileName?: string;         // Cache file prefix
  requestHeaders?: Record<string, string>;  // Custom headers
  checkOnLaunch?: boolean;        // Auto-check on mount (default: true)
  onUpdateAvailable?: (update: AppUpdateResponse) => void;
  onUpdateError?: (error: Error) => void;
  onDownloadProgress?: (progress: number) => void;
}

Backend Integration

Request (Sent to your API)

{
  "version": "1.0.0",
  "buildType": "production",
  "projectName": "MyApp",
  "packageName": "com.myapp"
}

Response (Your API should return)

{
  "updateAvailable": true,
  "latestVersion": "1.2.0",
  "mandatory": false,
  "downloadUrl": "https://cdn.example.com/app-v1.2.0.apk",
  "websiteUrl": "https://myapp.com/download"
}

When no update is available:

{
  "updateAvailable": false
}

Common Patterns

Download Progress

const { downloading, downloadProgress } = useAppUpdate({
  // ... config
  onDownloadProgress: (progress) => {
    console.log(`${Math.round(progress * 100)}%`);
  },
});

// In your UI
{downloading && downloadProgress !== null && (
  <Text>Downloading: {Math.round(downloadProgress * 100)}%</Text>
)}

Mandatory Updates

const { isMandatory, handleLater } = useAppUpdate({
  // ... config
});

// Disable "Later" button for mandatory updates
<Button 
  title="Later" 
  onPress={handleLater} 
  disabled={isMandatory}
/>

Error Handling

import { NetworkError, DownloadError } from "rojma-app-update";

const { error, errorMessage, retry } = useAppUpdate({
  // ... config
  onUpdateError: (err) => {
    if (err instanceof NetworkError) {
      console.error("Network error:", err.statusCode);
    } else if (err instanceof DownloadError) {
      console.error("Download failed:", err.message);
    }
  },
});

Error Types

import {
  AppUpdateError,      // Base error class
  UpdateCheckError,    // Update check failed
  DownloadError,       // APK download failed
  InstallationError,   // APK installation failed
  NetworkError,        // Network request failed
  ValidationError,     // Response validation failed
} from "rojma-app-update";

Platform Differences

The package automatically detects your environment and uses the appropriate APIs:

| Feature | Expo | React Native CLI | |---------|------|------------------| | File System | expo-file-system | react-native-fs | | APK Install | expo-intent-launcher | Native Linking | | URL Opening | expo-linking | React Native Linking |

Your code is identical on both platforms! The adapter pattern handles all platform differences automatically.

TypeScript

Full TypeScript support with exported types:

import type {
  AppUpdateConfig,
  AppUpdateResponse,
  UpdateState,
  UseAppUpdateReturn,
} from "rojma-app-update";

Troubleshooting

"expo-file-system is not installed"

You're using Expo. Install: npx expo install expo-file-system expo-intent-launcher expo-linking

"react-native-fs is not installed"

You're using React Native CLI. Install: npm install react-native-fs

APK installation fails (React Native CLI)

  • Ensure REQUEST_INSTALL_PACKAGES permission is in AndroidManifest.xml
  • Optionally install react-native-send-intent for better support

Permission denied

Ensure the permission is added to android/app/src/main/AndroidManifest.xml

Requirements

  • React Native 0.72+
  • Android only (iOS does not support APK installation)
  • Expo: SDK 50+
  • React Native CLI: react-native-fs package

License

MIT

More Information