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

platform-app-native-sdk-kit

v1.0.8

Published

Platform Native SDK Kit for MRI OTA Mono

Readme

Platform Native SDK Kit

Unified React Native SDK for MRI OTA Monorepo
Streamlined native features with built-in permission management

Version License Platform TypeScript

📦 20+ Native Modules🔐 6 Permission Helpers📱 iOS & Android🎯 TypeScript First


Table of Contents


Why This SDK?

The Problem

Building React Native apps with native features involves:

  • Managing 20+ separate dependencies with different APIs and patterns
  • Complex permission flows that require dozens of lines of boilerplate code
  • Version conflicts when multiple mini apps in a monorepo use different versions
  • Inconsistent UX when each team implements permissions differently
  • Difficult onboarding for new developers learning multiple APIs

The Solution

@platform-app/native-sdk-kit provides a unified interface to native features with built-in permission management, specifically designed for monorepo architectures.

How It Works

MRI-Mono-Repo/
├── main-container-app/          # 📦 Installs all peer dependencies once
├── platform-app-sdk-kit/        # 🔧 This SDK (declares peer dependencies)
└── mini-apps/                   # 🚀 Your feature apps (use the SDK)
    ├── feature-app-1/
    ├── feature-app-2/
    └── ...
  1. Main container installs all native dependencies (expo modules, react-native-date-picker, etc.)
  2. SDK package declares them as peer dependencies (doesn't install them)
  3. Mini apps import and use the SDK
  4. At runtime, mini apps inherit the peer dependencies from the main container

Benefits

Reduces permission handling code by 70% - Built-in helpers handle check + request flows
Eliminates version conflicts - Peer dependencies install once in main container
Standardizes patterns - Consistent API across all mini apps
Faster development - Start building features immediately
Type-safe - Full TypeScript definitions catch errors at compile time
Smaller bundles - No duplicate native modules across packages


Key Features

🔐 Permission Helpers

Simplified permission management for:

  • Speech Recognition (Microphone + Speech Recognizer)
  • Camera (Camera + Microphone for video)
  • Location (Foreground + Background)
  • Media Library (Read + Write, Limited Photos support)
  • Image Picker (Camera + Photo Library)
  • Audio Recording (Microphone + Audio Mode setup)

📅 Native Components

  • RNNativeDatePicker - Native date/time picker with modal support

📚 20+ Expo Modules

Pre-configured, namespaced exports:

Media: AV, Camera, Image, ImagePicker, ImageManipulator, Video, MediaLibrary
Files: FileSystem, DocumentPicker, Sharing
System: Clipboard, Device, Constants, Haptics, Linking, Localization
Communication: Location, MailComposer
Speech: Speech Recognition (re-exported)


Quick Start

Installation

# In your mini app
npm install @platform-app/native-sdk-kit

Basic Usage

import { 
  ImagePicker, 
  Location,
  FileSystem,
  ensureCameraPermissionForImagePicker 
} from '@platform-app/native-sdk-kit';

// Permission helper automatically checks + requests
const result = await ensureCameraPermissionForImagePicker();
if (result.granted) {
  const photo = await ImagePicker.launchCameraAsync({
    quality: 0.8,
    allowsEditing: true,
  });
  console.log('Photo taken:', photo.assets[0].uri);
}

Installation & Configuration

Architecture Overview

This SDK is designed for monorepo architectures where:

MRI-Mono-Repo/
├── main-container-app/          # Main Expo app
├── platform-app-sdk-kit/        # This SDK
└── mini-apps/                   # Feature packages
    ├── feature-app-1/
    ├── feature-app-2/
    └── ...

Key Concept:

  • Peer dependencies install once in the main container
  • Mini apps use the SDK without duplicating native modules
  • This prevents version conflicts and reduces bundle size

Step 1: Install Peer Dependencies (Main Container Only)

⚠️ IMPORTANT: Install peer dependencies ONLY in your main container app, not in the SDK or mini apps.

# Navigate to your main container app
cd main-container-app/

# Install all peer dependencies
npx expo install \
  react-native-date-picker \
  expo-av \
  expo-camera \
  expo-image \
  expo-image-picker \
  expo-image-manipulator \
  expo-media-library \
  expo-video \
  expo-document-picker \
  expo-file-system \
  expo-sharing \
  expo-clipboard \
  expo-constants \
  expo-device \
  expo-haptics \
  expo-linking \
  expo-localization \
  expo-location \
  expo-mail-composer \
  expo-speech-recognition

Step 2: Configure Permissions (Main Container)

In your main container's app.json, add the required plugins:

{
  "expo": {
    "name": "Main Container App",
    "plugins": [
      [
        "expo-camera",
        {
          "cameraPermission": "Allow $(PRODUCT_NAME) to access your camera",
          "microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone for video recording"
        }
      ],
      [
        "expo-location",
        {
          "locationAlwaysAndWhenInUsePermission": "Allow $(PRODUCT_NAME) to use your location",
          "locationAlwaysPermission": "Allow $(PRODUCT_NAME) to use your location in the background",
          "locationWhenInUsePermission": "Allow $(PRODUCT_NAME) to use your location while using the app"
        }
      ],
      [
        "expo-image-picker",
        {
          "photosPermission": "Allow $(PRODUCT_NAME) to access your photos",
          "cameraPermission": "Allow $(PRODUCT_NAME) to access your camera to take photos"
        }
      ],
      [
        "expo-media-library",
        {
          "photosPermission": "Allow $(PRODUCT_NAME) to access your photos",
          "savePhotosPermission": "Allow $(PRODUCT_NAME) to save photos to your library",
          "isAccessMediaLocationEnabled": true
        }
      ],
      [
        "expo-av",
        {
          "microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone for audio recording"
        }
      ]
    ]
  }
}

Step 3: Install SDK in Mini Apps

In each mini app/package that needs native features:

# Navigate to your mini app
cd mini-apps/feature-app-1/

# Install the SDK
npm install @platform-app/native-sdk-kit
# or
yarn add @platform-app/native-sdk-kit

✅ Do NOT install peer dependencies in mini apps - they automatically inherit them from the main container at runtime.


Step 4: Use the SDK in Mini Apps

// mini-apps/feature-app-1/src/CameraScreen.tsx
import { 
  ImagePicker, 
  Camera, 
  Location,
  ensureCameraPermissionForImagePicker,
  ensureForegroundLocationPermission
} from '@platform-app/native-sdk-kit';

export default function CameraScreen() {
  const takePhoto = async () => {
    // Permission helper checks + requests automatically
    const result = await ensureCameraPermissionForImagePicker();
    
    if (result.granted) {
      const photo = await ImagePicker.launchCameraAsync({
        quality: 0.8,
        allowsEditing: true,
      });
      console.log('Photo:', photo.assets[0].uri);
    } else if (!result.canAskAgain) {
      // User denied permanently - direct to settings
      Alert.alert(
        'Camera Permission Required',
        'Please enable camera access in Settings',
        [{ text: 'Open Settings', onPress: () => Linking.openSettings() }]
      );
    }
  };

  return (
    <Button title="Take Photo" onPress={takePhoto} />
  );
}

Step 5: Rebuild Main Container

After installing peer dependencies and configuring permissions:

# Navigate back to main container
cd main-container-app/

# Rebuild native code
npx expo prebuild --clean

# Run on iOS
npx expo run:ios

# Or run on Android
npx expo run:android
Comprehensive React Native SDK package providing native functionality through namespaced Expo modules:

- **RNNativeDatePicker** – Type-safe interface to `react-native-date-picker`
- **Permission helpers** – Internal helpers to check/request microphone and speech recognition permissions
- **Speech Recognition** – Complete speech-to-text functionality
- **Media & Camera** – Camera access, image/video picking, media library management, audio/video playback
- **File Management** – File system operations, document picking, image manipulation
- **System Integration** – Clipboard, sharing, haptics, linking, device info, location
- **Utilities** – Constants, localization, mail composer

All Expo modules are exported as **namespaces** to avoid naming conflicts and provide organized access to functionality.

## 📖 Documentation

- **[Quick Reference Guide](./docs/QUICK_REFERENCE.md)** - Fast examples and common patterns
- **[Complete API Documentation](./docs/API_DOCUMENTATION.md)** - Full API reference with UI styles
- **[Permission Helpers Usage](./docs/PERMISSION_HELPERS_USAGE.md)** - How to use permission helper utilities
- **[Permissions Guide](./docs/PERMISSIONS_GUIDE.md)** - Comprehensive permissions setup
- **[Permissions Required](./docs/PERMISSIONS_REQUIRED.md)** - Quick list of libraries needing permissions

## Installation

```bash
npm install platform-native-sdk-kit
# or
yarn add platform-native-sdk-kit

Peer Dependencies

This package requires the following peer dependencies (all use * version):

Core:

  • react
  • react-native
  • expo

Date & UI:

  • react-native-date-picker

Media & Camera:

  • expo-av - Audio/Video playback and recording
  • expo-camera - Camera access
  • expo-image - Optimized image component
  • expo-image-picker - Pick images/videos from library or camera
  • expo-image-manipulator - Resize, crop, rotate images
  • expo-media-library - Access device media library
  • expo-video - Video player component

File & Document:

  • expo-file-system - File system operations
  • expo-document-picker - Pick documents from device

System & Utilities:

  • expo-clipboard - Clipboard operations
  • expo-constants - System constants and app info
  • expo-device - Device information
  • expo-haptics - Haptic feedback
  • expo-linking - Deep linking and URL handling
  • expo-localization - Locale and timezone info
  • expo-location - Location services
  • expo-mail-composer - Compose and send emails
  • expo-sharing - Share files with other apps
  • expo-speech-recognition - Speech-to-text
# Install peer dependencies as needed
npm install expo expo-av expo-camera expo-image-picker # etc...

For Non-Monorepo Projects

If you're using this SDK in a standalone app (not a monorepo):

# Install the SDK
npm install @platform-app/native-sdk-kit

# Install peer dependencies in the same project
npx expo install react-native-date-picker expo-av expo-camera expo-image \
  expo-image-picker expo-image-manipulator expo-media-library expo-video \
  expo-document-picker expo-file-system expo-sharing expo-clipboard \
  expo-constants expo-device expo-haptics expo-linking expo-localization \
  expo-location expo-mail-composer expo-speech-recognition

# Configure app.json with plugins (see Step 2 above)

# Rebuild
npx expo prebuild
npx expo run:ios  # or run:android

Permission Helpers

The SDK includes 6 permission helper categories that simplify permission handling by automatically checking current status and requesting if needed.

Pattern

All helpers follow this pattern:

const result = await ensureXxxPermission();

if (result.granted) {
  // ✅ Permission granted - use the feature
} else if (result.canAskAgain) {
  // ⚠️ User denied but can ask again
  Alert.alert('Permission needed', 'This feature requires permission');
} else {
  // 🚫 User denied permanently - direct to settings
  Alert.alert(
    'Permission Required',
    'Please enable permission in Settings',
    [{ text: 'Open Settings', onPress: () => Linking.openSettings() }]
  );
}

1. Speech Recognition Permissions

import { 
  ensureSpeechRecognitionPermissions,
  checkMicrophonePermission,
  requestMicrophonePermission,
  useSpeechRecognitionEvent
} from '@platform-app/native-sdk-kit';

// All-in-one: checks + requests both microphone and speech recognizer
const result = await ensureSpeechRecognitionPermissions();
if (result.granted) {
  // Start speech recognition
}

// Check individual permissions
const micResult = await checkMicrophonePermission();
const speechResult = await checkSpeechRecognizerPermission();

// Request individual permissions
await requestMicrophonePermission();
await requestSpeechRecognizerPermission();

Use Cases: Voice commands, transcription, voice search


2. Camera Permissions

⚠️ Note: expo-camera uses hooks for permission handling. Use the Camera namespace hooks directly:

import { Camera } from '@platform-app/native-sdk-kit';

function CameraComponent() {
  const [cameraPermission, requestCameraPermission] = Camera.useCameraPermissions();
  const [micPermission, requestMicPermission] = Camera.useMicrophonePermissions();

  if (!cameraPermission?.granted) {
    return <Button onPress={requestCameraPermission} title="Grant Camera Permission" />;
  }

  return (
    <Camera.CameraView style={{ flex: 1 }} facing="back">
      {/* Camera UI */}
    </Camera.CameraView>
  );
}

Use Cases: Taking photos/videos, barcode scanning, AR features


3. Location Permissions

import { 
  ensureForegroundLocationPermission,
  ensureBackgroundLocationPermission,
  ensureFullLocationPermissions,
  Location
} from '@platform-app/native-sdk-kit';

// Foreground location (while app is open)
const result = await ensureForegroundLocationPermission();
if (result.granted) {
  const location = await Location.getCurrentPositionAsync({
    accuracy: Location.Accuracy.High,
  });
  console.log('Lat:', location.coords.latitude);
  console.log('Lon:', location.coords.longitude);
}

// Background location (requires foreground first)
const bgResult = await ensureBackgroundLocationPermission();

// Both foreground and background
const fullResult = await ensureFullLocationPermissions();
console.log('All granted:', fullResult.allGranted);

Use Cases: Maps, navigation, geofencing, location tracking


4. Media Library Permissions

import { 
  ensureMediaLibraryPermission,
  ensureFullMediaLibraryAccess,
  hasLimitedPhotoAccess,
  presentLimitedLibraryPicker,
  MediaLibrary
} from '@platform-app/native-sdk-kit';

// Read access
const readResult = await ensureMediaLibraryPermission(false);

// Write access (for saving photos)
const writeResult = await ensureMediaLibraryPermission(true);

// Full access (read + write)
const fullResult = await ensureFullMediaLibraryAccess();
if (fullResult.allGranted) {
  // Access all photos
  const assets = await MediaLibrary.getAssetsAsync({
    first: 20,
    mediaType: 'photo',
  });
}

// iOS 14+ Limited Photos
if (hasLimitedPhotoAccess(readResult)) {
  // User selected limited photos
  await presentLimitedLibraryPicker(); // Let user select more photos
}

Use Cases: Photo gallery, saving images, photo backup


5. Image Picker Permissions

import { 
  ensureCameraPermissionForImagePicker,
  ensureMediaLibraryPermissionForImagePicker,
  ensureFullImagePickerPermissions,
  hasLimitedPhotoAccessForImagePicker,
  ImagePicker
} from '@platform-app/native-sdk-kit';

// Camera permission (for taking photos)
const cameraResult = await ensureCameraPermissionForImagePicker();
if (cameraResult.granted) {
  const photo = await ImagePicker.launchCameraAsync({
    quality: 0.8,
    allowsEditing: true,
    aspect: [4, 3],
  });
}

// Media library permission (for picking photos)
const libraryResult = await ensureMediaLibraryPermissionForImagePicker();
if (libraryResult.granted) {
  const photo = await ImagePicker.launchImageLibraryAsync({
    allowsMultipleSelection: true,
    mediaTypes: 'images',
  });
}

// Both permissions
const fullResult = await ensureFullImagePickerPermissions();
console.log('All granted:', fullResult.allGranted);

Use Cases: Profile pictures, photo uploads, image selection


6. Audio Recording Permissions

import { 
  ensureAudioRecordingPermission,
  prepareForAudioRecording,
  setAudioModeForRecording,
  setAudioModeForPlayback,
  AV
} from '@platform-app/native-sdk-kit';

// Simple permission check + request
const result = await ensureAudioRecordingPermission();

// Prepare for recording (permission + audio mode)
const prepared = await prepareForAudioRecording();
if (prepared.canRecord) {
  const recording = new AV.Audio.Recording();
  await recording.prepareToRecordAsync(
    AV.Audio.RecordingOptionsPresets.HIGH_QUALITY
  );
  await recording.startAsync();
}

// Set audio mode manually
await setAudioModeForRecording({
  allowsRecordingIOS: true,
  playsInSilentModeIOS: true,
});

// Switch to playback mode
await setAudioModeForPlayback();

Use Cases: Voice memos, audio messages, podcasts


Components

RNNativeDatePicker

Native date and time picker component with modal support.

Props

interface RNNativeDatePickerProps {
  date: Date;                           // Current selected date
  onDateChange?: (date: Date) => void;  // Callback when date changes (inline mode)
  mode?: 'date' | 'time' | 'datetime'; // Picker mode (default: 'date')
  
  // Modal props
  modal?: boolean;                      // Show as modal
  open?: boolean;                       // Control modal visibility
  onConfirm?: (date: Date) => void;    // Callback when user confirms (modal)
  onCancel?: () => void;               // Callback when user cancels (modal)
  
  // Constraints
  minimumDate?: Date;                   // Minimum selectable date
  maximumDate?: Date;                   // Maximum selectable date
  
  // Localization
  locale?: string;                      // Locale for date formatting (e.g., 'en', 'es')
  
  // Styling (additional props from react-native-date-picker)
  title?: string;
  confirmText?: string;
  cancelText?: string;
  theme?: 'light' | 'dark' | 'auto';
}

Example: Modal Date Picker

import { useState } from 'react';
import { View, Button, Text } from 'react-native';
import { RNNativeDatePicker } from '@platform-app/native-sdk-kit';

export default function DatePickerExample() {
## Exports (from `index.ts`)

| Category | Namespace Export | Description |
|----------|------------------|-------------|
| **Date Picker** | `RNNativeDatePicker` | Native date/time picker component |
| **Permission Helpers** | Direct exports | Speech and microphone permission utilities |
| **Speech Recognition** | Direct exports | All exports from `expo-speech-recognition` |
| **Media & Camera** | `AV`, `Video`, `ImagePicker`, `Image`, `MediaLibrary` | Audio/video playback, camera, image operations |
| **File & Document** | `FileSystem`, `DocumentPicker`, `Image` | File operations, document selection |
| **System Integration** | `Clipboard`, `Sharing`, `Haptics`, `Linking`, `MailComposer` | System-level integrations |
| **Device & Utilities** | `Device`, `Constants`, `Localization`, `Location` | Device info, app constants, locale, GPS |

---

## Usage

### RNNativeDatePicker

```tsx
import React, { useState } from 'react';
import { View, Button } from 'react-native';
import { RNNativeDatePicker } from 'platform-native-sdk-kit';

export default function App() {
  const [date, setDate] = useState(new Date());
  const [open, setOpen] = useState(false);

  return (
    <View>
      <Button title="Select Date" onPress={() => setOpen(true)} />
      <Text>Selected: {date.toLocaleDateString()}</Text>
      
      <Button title="Open Date Picker" onPress={() => setOpen(true)} />
      <RNNativeDatePicker
        modal
        open={open}
        date={date}
        onConfirm={(selectedDate) => {
          setOpen(false);
          setDate(selectedDate);
        }}
        onCancel={() => setOpen(false)}
      />
    </View>
  );
}

Example: Time Picker

const [time, setTime] = useState(new Date());
const [showPicker, setShowPicker] = useState(false);

<RNNativeDatePicker
  modal
  open={showPicker}
  date={time}
  mode="time"
  onConfirm={(selectedTime) => {
    setTime(selectedTime);
    setShowPicker(false);
  }}
  onCancel={() => setShowPicker(false)}
/>

Example: DateTime Picker with Constraints

const [dateTime, setDateTime] = useState(new Date());
const [visible, setVisible] = useState(false);

const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);

const nextMonth = new Date();
nextMonth.setMonth(nextMonth.getMonth() + 1);

<RNNativeDatePicker
  modal
  open={visible}
  date={dateTime}
  mode="datetime"
  minimumDate={tomorrow}
  maximumDate={nextMonth}
  onConfirm={setDateTime}
  onCancel={() => setVisible(false)}
  title="Select appointment time"
  confirmText="Book"
  cancelText="Close"
/>

Available Modules

All modules are exported with namespaces to avoid naming conflicts.

Media & Camera

| Module | Description | Key Functions | |--------|-------------|---------------| | AV | Audio/Video playback & recording | Audio.Sound(), Audio.Recording() | | Camera | Camera access | CameraView, useCameraPermissions() | | Image | Optimized image component | <Image /> component | | ImagePicker | Pick/take photos | launchCameraAsync(), launchImageLibraryAsync() | | ImageManipulator | Image editing | manipulateAsync() (resize, rotate, crop) | | Video | Video player | <VideoView />, useVideoPlayer() | | MediaLibrary | Photo library access | getAssetsAsync(), saveToLibraryAsync() |

File Management

| Module | Description | Key Functions | |--------|-------------|---------------| | FileSystem | File operations | readAsStringAsync(), writeAsStringAsync(), downloadAsync() | | DocumentPicker | Document selection | getDocumentAsync() | | Sharing | Share files/content | shareAsync() |

System Integration

| Module | Description | Key Functions | |--------|-------------|---------------| | Clipboard | Copy/paste | setStringAsync(), getStringAsync() | | Device | Device information | deviceName, osVersion, modelName | | Constants | App constants | expoConfig, systemVersion | | Haptics | Vibration feedback | impactAsync(), notificationAsync() | | Linking | URLs & deep links | openURL(), canOpenURL(), openSettings() | | Localization | Locale information | locale, timezone, isRTL |

Communication

| Module | Description | Key Functions | |--------|-------------|---------------| | Location | GPS & geolocation | getCurrentPositionAsync(), watchPositionAsync() | | MailComposer | Email composition | composeAsync() |

Speech

All exports from expo-speech-recognition are re-exported directly:

import { 
  useSpeechRecognitionEvent,
  ExpoSpeechRecognitionModule
} from '@platform-app/native-sdk-kit';

📚 For detailed API documentation of each module, see:


Common Use Cases

Taking and Saving a Photo

import { 
  ImagePicker, 
  MediaLibrary,
  ensureCameraPermissionForImagePicker,
  ensureMediaLibraryPermission 
} from '@platform-app/native-sdk-kit';

async function takeAndSavePhoto() {
  // Check camera permission
  const cameraResult = await ensureCameraPermissionForImagePicker();
  if (!cameraResult.granted) {
    Alert.alert('Camera permission required');
    return;
  }

  // Take photo
  const photo = await ImagePicker.launchCameraAsync({
    quality: 0.8,
    allowsEditing: true,
    aspect: [4, 3],
  });

  if (photo.canceled) return;

  // Save to library
  const libraryResult = await ensureMediaLibraryPermission(true);
  if (libraryResult.granted) {
    await MediaLibrary.saveToLibraryAsync(photo.assets[0].uri);
    Alert.alert('Success', 'Photo saved to library!');
  }
}

Recording Audio

import { 
  prepareForAudioRecording,
  setAudioModeForPlayback,
  AV 
} from '@platform-app/native-sdk-kit';

async function recordAudio() {
  // Prepare for recording (permission + audio mode)
  const result = await prepareForAudioRecording();
  
  if (!result.canRecord) {
    Alert.alert('Microphone permission required');
    return;
  }

  // Start recording
  const recording = new AV.Audio.Recording();
  await recording.prepareToRecordAsync(
    AV.Audio.RecordingOptionsPresets.HIGH_QUALITY
  );
  await recording.startAsync();
  
  // ... later, stop recording
  await recording.stopAndUnloadAsync();
  const uri = recording.getURI();
  
  // Play back
  await setAudioModeForPlayback();
  const sound = new AV.Audio.Sound();
  await sound.loadAsync({ uri });
  await sound.playAsync();
  
  // Cleanup
  await sound.unloadAsync();
}

Getting Location

import { 
  ensureForegroundLocationPermission,
  Location 
} from '@platform-app/native-sdk-kit';

async function getUserLocation() {
  const result = await ensureForegroundLocationPermission();
  
  if (!result.granted) {
    Alert.alert('Location permission required');
    return;
  }

  const location = await Location.getCurrentPositionAsync({
    accuracy: Location.Accuracy.High,
  });

  console.log('Latitude:', location.coords.latitude);
  console.log('Longitude:', location.coords.longitude);
  console.log('Altitude:', location.coords.altitude);
  
  // Watch location changes
  const subscription = await Location.watchPositionAsync(
    {
      accuracy: Location.Accuracy.High,
      distanceInterval: 10, // Update every 10 meters
    },
    (newLocation) => {
      console.log('New location:', newLocation.coords);
    }
  );
  
  // Cleanup
  // subscription.remove();
}

File Operations

import { FileSystem } from '@platform-app/native-sdk-kit';

async function fileOperations() {
  // Define file path
  const filePath = FileSystem.documentDirectory + 'data.json';
  
  // Write data
  const data = { user: 'John', timestamp: Date.now() };
  await FileSystem.writeAsStringAsync(
    filePath,
    JSON.stringify(data, null, 2)
  );
  
  // Read data
  const content = await FileSystem.readAsStringAsync(filePath);
  const parsedData = JSON.parse(content);
  console.log('Read data:', parsedData);
  
  // Check if file exists
  const fileInfo = await FileSystem.getInfoAsync(filePath);
  console.log('File exists:', fileInfo.exists);
  console.log('File size:', fileInfo.size);
  
  // Download file
  const downloadResult = await FileSystem.downloadAsync(
    'https://example.com/file.pdf',
    FileSystem.documentDirectory + 'download.pdf'
  );
  console.log('Downloaded to:', downloadResult.uri);
  
  // Delete file
  await FileSystem.deleteAsync(filePath);
}

Clipboard Operations

import { Clipboard } from '@platform-app/native-sdk-kit';

async function clipboardOperations() {
  // Copy text
  await Clipboard.setStringAsync('Hello, world!');
  
  // Paste text
  const text = await Clipboard.getStringAsync();
  console.log('Clipboard:', text);
  
  // Check if clipboard has content
  const hasContent = await Clipboard.hasStringAsync();
  if (hasContent) {
    console.log('Clipboard has content');
  }
  
  // Copy URL
  await Clipboard.setUrlAsync('https://example.com');
}

Sharing Content

import { Sharing, FileSystem } from '@platform-app/native-sdk-kit';

async function shareContent() {
  // Share text (save as file first)
  const textPath = FileSystem.cacheDirectory + 'share.txt';
  await FileSystem.writeAsStringAsync(textPath, 'Text to share');
  await Sharing.shareAsync(textPath);
  
  // Share file with options
  await Sharing.shareAsync(fileUri, {
    mimeType: 'application/pdf',
    dialogTitle: 'Share Document',
    UTI: 'public.pdf', // iOS only
  });
}

Haptic Feedback

import { Haptics } from '@platform-app/native-sdk-kit';

function hapticExamples() {
  // Button press feedback
  await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
  await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
  await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy);
  
  // Notification feedback
  await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
  await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning);
  await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
  
  // Selection feedback (e.g., picker wheel)
  await Haptics.selectionAsync();
}

Opening URLs

import { Linking } from '@platform-app/native-sdk-kit';

async function openLinks() {
  // Open website
  await Linking.openURL('https://example.com');
  
  // Make phone call
  const phoneNumber = '+1234567890';
  const canCall = await Linking.canOpenURL(`tel:${phoneNumber}`);
  if (canCall) {
    await Linking.openURL(`tel:${phoneNumber}`);
  }
  
  // Send email
  await Linking.openURL('mailto:[email protected]?subject=Help&body=Hello');
  
  // Open maps
  await Linking.openURL('geo:37.7749,-122.4194');
  
  // Open app settings
  await Linking.openSettings();
}

Permission Handling

Permission Result Interface

All permission helpers return a consistent interface:

interface PermissionResult {
  granted: boolean;        // Whether permission is granted
  canAskAgain: boolean;    // Whether we can request again
  status: 'granted' | 'denied' | 'undetermined';
  response: PermissionResponseLike;  // Raw response
}

Handling Permission States

const result = await ensureCameraPermission();

if (result.granted) {
  // ✅ Permission granted - use the feature
  await Camera.takePictureAsync();
  
} else if (result.canAskAgain) {
  // ⚠️ User denied but can ask again
  Alert.alert(
    'Camera Permission',
    'This feature requires camera access',
    [
      { text: 'Cancel', style: 'cancel' },
      { text: 'Try Again', onPress: () => requestCameraPermission() }
    ]
  );
  
} else {
  // 🚫 User denied permanently - direct to settings
  Alert.alert(
    'Camera Permission Required',
    'Please enable camera access in Settings to use this feature',
    [
      { text: 'Cancel', style: 'cancel' },
      { text: 'Open Settings', onPress: () => Linking.openSettings() }
    ]
  );
}

iOS 14+ Limited Photos Access

When users select "Limited Photos" on iOS 14+:

import { 
  ensureMediaLibraryPermissionForImagePicker,
  hasLimitedPhotoAccessForImagePicker,
  ImagePicker 
} from '@platform-app/native-sdk-kit';

const result = await ensureMediaLibraryPermissionForImagePicker();

if (result.granted) {
  if (hasLimitedPhotoAccessForImagePicker(result)) {
    // User selected limited photos
    Alert.alert(
      'Limited Photos Access',
      'You have limited photo access. Select more photos?',
      [
        { text: 'No', style: 'cancel' },
        { 
          text: 'Select Photos', 
          onPress: async () => {
            // This opens iOS photo selector to add more photos
            await presentLimitedLibraryPicker();
          }
        }
      ]
    );
  }
  
  // Continue with image picker
  const photo = await ImagePicker.launchImageLibraryAsync();
}

Best Practices

✅ Do

// Use namespace imports
import { FileSystem, ImagePicker } from '@platform-app/native-sdk-kit';
FileSystem.writeAsStringAsync(...);

// Use permission helpers
const result = await ensureCameraPermission();
if (result.granted) {
  // Use feature
}

// Handle all permission states
if (!result.canAskAgain) {
  // Direct to settings
  Linking.openSettings();
}

// Clean up resources
const sound = new AV.Audio.Sound();
await sound.loadAsync(require('./audio.mp3'));
await sound.playAsync();
// ... later
await sound.unloadAsync(); // ✅ Always unload

// Handle errors
try {
  const photo = await ImagePicker.launchCameraAsync();
} catch (error) {
  console.error('Camera error:', error);
  Alert.alert('Error', 'Failed to access camera');
}

// Use TypeScript for type safety
const location: Location.LocationObject = await Location.getCurrentPositionAsync();

❌ Don't

// Don't destructure directly from package
import { writeAsStringAsync } from '@platform-app/native-sdk-kit'; // ❌ Won't work

// Don't skip permission checks
await Camera.takePictureAsync(); // ❌ No permission check

// Don't forget cleanup
const sound = new AV.Audio.Sound();
await sound.playAsync(); // ❌ Never unloaded (memory leak)

// Don't ignore errors
const photo = await ImagePicker.launchCameraAsync(); // ❌ No error handling

// Don't forget to check canAskAgain
if (!result.granted) {
  Alert.alert('Permission denied'); // ❌ Doesn't handle permanent denial
}

Memory Management

// Audio cleanup
const sound = new AV.Audio.Sound();
await sound.loadAsync(require('./audio.mp3'));
await sound.playAsync();
// ... when done
await sound.unloadAsync(); // ✅

// Location subscription cleanup
const subscription = await Location.watchPositionAsync(...);
// ... when done
subscription.remove(); // ✅

// Use in React components
useEffect(() => {
  const subscription = Location.watchPositionAsync(...);
  
  return () => {
    subscription.then(sub => sub.remove()); // ✅ Cleanup on unmount
  };
}, []);

Performance Tips

  1. Lazy load audio files - Only load when needed
  2. Compress images - Use quality option in ImagePicker (0.7-0.8 recommended)
  3. Throttle location updates - Use distanceInterval option to reduce updates
  4. Cache files - Use FileSystem.cacheDirectory for temporary files
  5. Clean up subscriptions - Always remove event listeners and subscriptions

Troubleshooting

"Module has no exported member"

Problem:

import { writeAsStringAsync } from '@platform-app/native-sdk-kit'; // ❌ Error

Solution:

import { FileSystem } from '@platform-app/native-sdk-kit'; // ✅ Use namespace
FileSystem.writeAsStringAsync(...);

All modules are namespaced - import the namespace, not individual functions.


Permission Always Denied

Problem: Permission request always returns denied.

Solution:

const result = await Camera.getCameraPermissionsAsync();

if (!result.canAskAgain) {
  // User denied permanently - direct to settings
  Alert.alert(
    'Permission Required',
    'Please enable camera access in Settings',
    [{ text: 'Open Settings', onPress: () => Linking.openSettings() }]
  );
}

Peer Dependencies Not Found

Problem: Cannot find module 'expo-camera' or similar error in mini apps.

Solution:

  • Ensure peer dependencies are installed in the main container app
  • Mini apps inherit them at runtime through the monorepo structure
  • Verify your build process correctly resolves peer dependencies
  • Try rebuilding: cd main-container-app && npx expo prebuild --clean

TypeScript Errors

Problem: TypeScript can't find types.

Solution:

  1. Restart TypeScript server: Cmd+Shift+P → "TypeScript: Restart TS Server"
  2. Check tsconfig.json includes the SDK
  3. Ensure @types/react and @types/react-native are installed
  4. Use autocomplete/IntelliSense to discover available functions

Audio Won't Play

Problem: Audio playback fails silently.

Solution:

// Set audio mode first
await AV.Audio.setAudioModeAsync({
  playsInSilentModeIOS: true,
  shouldDuckAndroid: true,
  staysActiveInBackground: false,
});

// Then play
const sound = new AV.Audio.Sound();
await sound.loadAsync(require('./audio.mp3'));
await sound.playAsync();

Or use the helper:

import { setAudioModeForPlayback } from '@platform-app/native-sdk-kit';

await setAudioModeForPlayback();
// Then play audio

Camera Shows Black Screen

Problem: Camera view is black or doesn't load.

Solutions:

  1. Check permissions:
const [permission, requestPermission] = Camera.useCameraPermissions();
if (!permission?.granted) {
  await requestPermission();
}
  1. Test on real device - Camera doesn't work in iOS Simulator

  2. Check app.json configuration:

{
  "expo": {
    "plugins": [
      [
        "expo-camera",
        {
          "cameraPermission": "Allow app to access camera"
        }
      ]
    ]
  }
}
  1. Rebuild native code:
npx expo prebuild --clean
npx expo run:ios

File Not Found

Problem: FileSystem operations fail with "file not found".

Solution:

// Always use full paths
const filePath = FileSystem.documentDirectory + 'file.txt';

// Check if file exists first
const fileInfo = await FileSystem.getInfoAsync(filePath);
if (fileInfo.exists) {
  // File exists - read it
  const content = await FileSystem.readAsStringAsync(filePath);
} else {
  // Create file first
  await FileSystem.writeAsStringAsync(filePath, 'Initial content');
}

// Create directory if needed
await FileSystem.makeDirectoryAsync(
  FileSystem.documentDirectory + 'myFolder/',
  { intermediates: true }
);

API Reference

For detailed API documentation, see:


Package Information

  • Package Name: @platform-app/native-sdk-kit
  • Version: 1.0.6
  • License: MIT
  • Author: MRI Software
  • TypeScript: ✅ Full support with type definitions

Summary

This SDK provides:

20+ Native Modules - Camera, Location, Files, Audio, Video, and more
6 Permission Helper Categories - Simplified permission management
Monorepo Optimized - Peer dependencies install once in main container
Type-Safe - Full TypeScript definitions
Cross-Platform - iOS & Android support
Production Ready - Used in MRI OTA Monorepo

Get started now:

# In your mini app
npm install @platform-app/native-sdk-kit
import { ImagePicker, ensureCameraPermissionForImagePicker } from '@platform-app/native-sdk-kit';

const result = await ensureCameraPermissionForImagePicker();
if (result.granted) {
  const photo = await ImagePicker.launchCameraAsync();
}

Happy coding! 🚀


MIT License © 2024 MRI Software

Permission helpers (speech / microphone)

Request or check permissions before starting speech recognition:

import {
  ensureSpeechRecognitionPermissions,
  type SpeechRecognitionPermissionResult,
} from 'platform-native-sdk-kit';

// Before starting recognition: check, then request if needed
const result = await ensureSpeechRecognitionPermissions();
if (result.granted) {
  // Start speech recognition
} else if (!result.canAskAgain) {
  // Open app settings
} else {
  // User denied; show message
}

Available helpers:

  • checkSpeechRecognitionPermissions() – Check both speech + microphone (no dialog).
  • requestSpeechRecognitionPermissions() – Request both (shows system dialog).
  • ensureSpeechRecognitionPermissions() – Check first; request if not granted.
  • checkMicrophonePermission() / requestMicrophonePermission() – Microphone only.
  • checkSpeechRecognizerPermission() / requestSpeechRecognizerPermission() – Speech recognizer only (mainly iOS).

SpeechRecognitionPermissionResult: { granted, canAskAgain, status, response }.

Speech recognition (expo-speech-recognition)

All APIs from expo-speech-recognition are re-exported. Use them as you would from that package:

import {
  ExpoSpeechRecognitionModule,
  useSpeechRecognitionEvent,
} from 'platform-native-sdk-kit';

See expo-speech-recognition for full API and setup (e.g. config plugin, permissions in app.json).

Namespaced Expo Modules

All Expo modules are exported as namespaces. Import and use them with their namespace:

import { 
  FileSystem, 
  Clipboard, 
  ImagePicker, 
  Sharing,
  AV,
  Device,
  Location 
} from '@platform-app/native-sdk-kit';

// File System
const file = new FileSystem.File(FileSystem.Paths.cache, 'example.txt');
await file.write('Hello, world!');
const content = await file.text();

// Clipboard
await Clipboard.setStringAsync('Copied text');
const text = await Clipboard.getStringAsync();

// Image Picker
const result = await ImagePicker.launchImageLibraryAsync({
  mediaTypes: ImagePicker.MediaTypeOptions.Images,
});

// Sharing
await Sharing.shareAsync(fileUri, {
  mimeType: 'application/pdf',
  dialogTitle: 'Share this document'
});

// Audio/Video
const sound = new AV.Audio.Sound();
await sound.loadAsync(require('./assets/sound.mp3'));
await sound.playAsync();

// Device Info
const deviceName = Device.deviceName;
const osVersion = Device.osVersion;

// Location
const location = await Location.getCurrentPositionAsync({
  accuracy: Location.Accuracy.High
});

Quick Reference - Namespaced Modules

| Namespace | Use Case | Key Functions | |-----------|----------|---------------| | AV | Audio/video playback & recording | Audio.Sound(), Video component | | Clipboard | Copy/paste operations | setStringAsync(), getStringAsync() | | Constants | App and system info | expoConfig, systemVersion | | Device | Device information | deviceName, osVersion, modelName | | DocumentPicker | Pick documents | getDocumentAsync() | | FileSystem | File operations | File, Directory, Paths | | Haptics | Vibration feedback | impactAsync(), notificationAsync() | | Image | Optimized images | <Image /> component | | ImagePicker | Pick/take photos | launchImageLibraryAsync(), launchCameraAsync() | | Linking | URLs and deep links | openURL(), canOpenURL() | | Localization | Locale info | locale, timezone, isRTL | | Location | GPS and positioning | getCurrentPositionAsync() | | MailComposer | Send emails | composeAsync() | | MediaLibrary | Access photos/videos | getAssetsAsync(), createAssetAsync() | | Sharing | Share files | shareAsync(), isAvailableAsync() | | Video | Video player | <Video /> component |

See the API_DOCUMENTATION.md for detailed examples and all available APIs.


API

RNNativeDatePicker

Accepts all props from react-native-date-picker.

| Prop | Type | Description | |------|------|--------------| | modal | boolean | Display as modal | | open | boolean | Modal open state | | date | Date | Currently selected date | | onConfirm | (date: Date) => void | Called when date is confirmed | | onCancel | () => void | Called when picker is cancelled | | mode | 'date' | 'time' | 'datetime' | Picker mode | | locale | string | Locale for date formatting |

TypeScript

This package is written in TypeScript and ships with type definitions.

License

MIT ©

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Issues

If you encounter any problems, please file an issue along with a detailed description.