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

milton-fcm-client-sdk

v1.1.4

Published

Milton Health Coach FCM Client SDK for React Native - Async request processing with Firebase Cloud Messaging push notifications and intelligent polling coordination

Readme

Milton Health Coach FCM Client SDK

A React Native FCM Client SDK for the Milton Health Coach platform that provides asynchronous request processing with intelligent polling and mandatory push notification support for optimal background processing.

🚨 FCM Required

Firebase Cloud Messaging (FCM) is mandatory for this SDK. The SDK is specifically designed to handle Milton's async APIs efficiently using push notifications for background processing. Without FCM, the SDK will not function.

Features

  • Async Request Processing: Submit requests and get immediate responses
  • Intelligent Polling: Configurable exponential backoff polling (default: 5s, 10s, 20s, 40s, 80s, 120s)
  • Push Notifications: Firebase Cloud Messaging integration for instant results (REQUIRED)
  • Unified Coordination: Automatically stops polling when push notification received
  • Background Handling: Continues processing when app is backgrounded
  • Battery Optimized: Smart polling intervals and background management
  • Offline Support: Request queuing and automatic retry when network restored
  • Network Resilience: Handles network failures gracefully with persistent storage
  • JavaScript: Pure JavaScript implementation for React Native projects
  • Fully Configurable: Customize polling intervals, retry attempts, timeouts, and offline behavior
  • Automatic Client ID: Generates unique client identifiers for request tracking

📋 Prerequisites

Before installing the SDK, you MUST have:

  • ✅ Firebase project created
  • ✅ Android/iOS apps added to Firebase
  • ✅ Push notification certificates/keys configured
  • ✅ React Native 0.60.0 or higher

Installation

Step 1: Install the SDK

npm install milton-fcm-client-sdk

Step 2: Install Required Dependencies

npm install @react-native-firebase/app @react-native-firebase/messaging @react-native-async-storage/async-storage @react-native-community/netinfo react-native-device-info

Step 3: Verify Installation

npm run verify

This will check your setup and provide guidance on any missing dependencies or configuration.

Step 3: Firebase Configuration (MANDATORY)

iOS Setup

  1. Add GoogleService-Info.plist to your iOS project
  2. Enable push notifications capability in Xcode
  3. Configure APNs certificates or keys in Firebase Console
  4. Follow our detailed Firebase Setup Guide

Android Setup

  1. Add google-services.json to android/app/
  2. Update build.gradle files with Firebase plugin
  3. Configure FCM in Firebase Console
  4. Follow our detailed Firebase Setup Guide

Step 4: Verify Installation

import { MiltonAsyncClient } from 'milton-fcm-client-sdk';

// This will fail if FCM is not properly configured
const client = new MiltonAsyncClient({
  baseUrl: 'https://api.milton.com',
  apiKey: 'your-api-key'
  // enablePushNotifications defaults to true and is required
});

🔥 Firebase Setup Guide

FCM setup is mandatory. Choose your setup approach:

📋 Quick Setup Checklist

Use our FCM Setup Checklist for a step-by-step verification process.

📖 Detailed Setup Guide

See our comprehensive Firebase Setup Guide for detailed instructions including:

  • Creating Firebase project
  • Configuring Android and iOS apps
  • Setting up push notification certificates
  • Testing FCM integration

🚀 Complete API Examples

See All Milton APIs Example for a complete React Native component demonstrating all three async APIs:

  • User Message Processing
  • Survey Processing
  • Fitness Calculations

Quick Start

⚠️ Ensure Firebase is configured before using the SDK - See Firebase Setup Guide

import { MiltonAsyncClient } from 'milton-fcm-client-sdk';

// Basic configuration (FCM enabled by default - REQUIRED)
const client = new MiltonAsyncClient({
  baseUrl: 'https://api.milton.com',
  apiKey: 'your-api-key'
  // enablePushNotifications: true (default - MANDATORY)
});

// Full configuration with all Milton async APIs
const client = new MiltonAsyncClient({
  baseUrl: 'https://api.milton.com',
  apiKey: 'your-api-key',
  enablePushNotifications: true,        // MANDATORY - DO NOT SET TO FALSE
  clientIdConfig: {
    autoGenerate: true,                 // Auto-generate client ID
    prefix: 'myapp',                    // Custom prefix
    includeDeviceInfo: true,            // Include device info
    includePlatform: true,              // Include platform (iOS/Android)
    includeAppVersion: true             // Include app version
  },
  pollingConfig: {
    intervals: [5, 10, 20, 40, 80, 120], // Custom polling intervals
    maxAttempts: 6,                     // Custom max attempts
    batteryOptimized: true              // Optimize for battery life
  },
  offlineConfig: {
    maxQueueSize: 100,                  // Custom queue size
    retryAttempts: 3                    // Custom retry attempts
  }
});

// Example 1: User Message Processing
const userResponse = await client.submitUserMessage({
  orgId: 123,
  userId: 456,
  question: "Log my breakfast: scrambled eggs and toast",
  image: imageUri, // optional
}, {
  onProgress: (status) => {
    console.log('Processing status:', status.status);
  },
  onComplete: (result) => {
    console.log('Analysis complete:', result);
  },
  onError: (error) => {
    console.error('Processing failed:', error);
  }
});

// Example 2: Survey Processing
const surveyResponse = await client.submitSurvey({
  phone_number: "+1234567890",
  survey: "Q. What's your gender?\nA. Male\n\nQ. What's your current weight?\nA. 173 lbs\n\nQ. How tall are you?\nA. 5 ft 11 in\n\nQ. What's your primary health goal right now?\nA. Lose weight\n\nQ. When do you want to reach your goal?\nA. 12/29/2025\n\nQ. What five things have you eaten the most over the last two to four weeks?\nA. I've eaten Paneer curry and rice, Dal and rice, homemade pizza, potato curry\n\nQ. Any food allergies, intolerances or other reasons you avoid foods?\nA. \n\nQ. How many meals do you usually eat in a day?\nA. 3\n\nQ. How often do you cook at home vs. eat out?\nA. Mostly home\n\nQ. How many days per week are you currently active?\nA. 6\n\nQ. How long are your typical workouts?\nA. 45-60 minutes\n\nQ. What is the intensity?\nA. Low: Light activity, easy pace\n\nQ. Any current or past injuries we should know about?\nA. \n\nQ. Do you have any chronic conditions we should consider when creating your plan?\nA. None of the above\n\nQ. Have you ever been told by a doctor to avoid certain types of exercise?\nA. Yes: Heavy weightlifting and water sports",
  birthday: "1990-01-01",
  default_timezone: "UTC-6"
}, {
  onComplete: (result) => {
    console.log('Survey processed:', result);
  }
});

// Example 3: Fitness Calculation
const fitnessResponse = await client.calculateFitness({
  orgId: 123,
  userId: 456,
  fitnessData: {
    steps: 8500,
    heart_rate_avg: 72,
    active_minutes: 45,
    calories_burned: 320
  }
}, {
  onComplete: (result) => {
    console.log('Fitness calculated:', result);
  }
});

console.log('All requests submitted with push notification support');

Configuration

The SDK is fully configurable with custom polling intervals, retry attempts, and offline settings. See the Configuration Guide for complete details on all available parameters.

Key Configurable Parameters

  • baseUrl & apiKey: Required connection parameters
  • clientIdConfig.autoGenerate: Auto-generate unique client IDs (default: true)
  • clientIdConfig.prefix: Custom prefix for generated client IDs (default: 'milton')
  • pollingConfig.intervals: Custom polling intervals (default: [5, 10, 20, 40, 80, 120])
  • pollingConfig.maxAttempts: Maximum polling attempts (default: 6)
  • offlineConfig.maxQueueSize: Offline queue size (default: 50)
  • offlineConfig.retryAttempts: Retry attempts for failed requests (default: 3)

API Reference

MiltonAsyncClient

Constructor

new MiltonAsyncClient(config: MiltonSDKConfig)

Config Options:

  • baseUrl (string): Milton API base URL
  • apiKey (string): Your API key
  • timeout (number, optional): Request timeout in ms (default: 30000)
  • enablePushNotifications (boolean, optional): Enable FCM push notifications (default: true)
  • pollingConfig (PollingConfig, optional): Custom polling configuration
  • offlineConfig (OfflineConfig, optional): Offline handling configuration

Methods

submitUserMessage(request, options?)

Submit a user message for async processing.

await client.submitUserMessage({
  orgId: 123,
  userId: 456,
  question: "What should I eat for lunch?",
  image: "data:image/jpeg;base64,..." // optional
}, {
  onProgress: (status) => console.log(status),
  onComplete: (result) => console.log(result),
  onError: (error) => console.error(error)
});
submitSurvey(request, options?)

Submit survey data for async processing.

await client.submitSurvey({
  phone_number: "+1234567890",
  survey: "Mood: good, Energy: 8/10, Sleep: 7 hours",
  birthday: "1990-01-01",
  default_timezone: "America/New_York"
}, {
  onComplete: (result) => console.log('Survey processed:', result)
});
calculateFitness(request, options?)

Submit fitness calculation request.

await client.calculateFitness({
  orgId: 123,
  userId: 456,
  fitnessData: {
    steps: 8500,
    heart_rate: 72,
    activity_minutes: 45
  }
}, {
  onComplete: (result) => console.log('Fitness calculated:', result)
});
getRequestStatus(requestId)

Get current status of a request.

const status = await client.getRequestStatus('request-uuid');
console.log('Status:', status.status); // 'queued', 'processing', 'completed', 'failed'
cancelRequest(requestId)

Cancel a pending or processing request.

await client.cancelRequest('request-uuid');
getOfflineQueueStatus()

Get current offline queue and network status.

const status = client.getOfflineQueueStatus();
console.log('Queue length:', status.queueLength);
console.log('Is online:', status.isOnline);
console.log('Active polling:', status.activePollingRequests);
clearOfflineQueue()

Clear all queued offline requests.

await client.clearOfflineQueue();
dispose()

Clean up resources when done with the client.

client.dispose();

Client ID Management

The SDK automatically generates and manages unique client identifiers to help track requests across different devices and app instances.

Automatic Client ID Generation

By default, the SDK generates a unique client ID that includes:

  • Custom prefix (default: 'milton')
  • Platform (iOS/Android)
  • App version
  • Device identifier
  • Timestamp

Example generated ID: milton-ios-v1.2.3-abc12345-1k2j3h4g

Configuration Options

const client = new MiltonAsyncClient({
  baseUrl: 'https://api.milton.com',
  apiKey: 'your-api-key',
  clientIdConfig: {
    autoGenerate: true,           // Enable auto-generation (default: true)
    prefix: 'myapp',             // Custom prefix (default: 'milton')
    includeDeviceInfo: true,     // Include device info (default: true)
    includePlatform: true,       // Include platform (default: true)
    includeAppVersion: true,     // Include app version (default: true)
    customClientId: null,        // Use custom ID instead of auto-generation
    storageKey: 'my_client_id'   // Custom storage key (default: 'milton_client_id')
  }
});

Manual Client ID Management

// Get current client ID
const clientId = client.getClientId();
console.log('Current client ID:', clientId);

// Set custom client ID
await client.setClientId('my-custom-client-id');

// Regenerate client ID
await client.regenerateClientId();

// Override per request
await client.submitUserMessage({
  orgId: 123,
  userId: 456,
  question: "What should I eat?"
}, {
  clientId: 'request-specific-id' // Override auto-generated ID
});

Persistent Storage

Client IDs are automatically stored in AsyncStorage and persist across app restarts. The same client ID will be used until:

  • The app is uninstalled
  • You manually regenerate it
  • You set a custom client ID

How It Works

Unified Polling + Push Strategy

The SDK uses a smart coordination strategy:

  1. Submit Request: Returns immediately with request ID
  2. Start Polling: Begins intelligent polling with exponential backoff
  3. Push Notification: Server sends push when processing completes
  4. Stop Polling: SDK automatically stops polling when push received
  5. Fallback: If push fails, polling continues until completion

Polling Intervals

Default exponential backoff: [1, 2, 4, 8, 15, 30] seconds

  • Attempt 1: Poll after 1 second
  • Attempt 2: Poll after 2 seconds
  • Attempt 3: Poll after 4 seconds
  • Attempt 4: Poll after 8 seconds
  • Attempt 5: Poll after 15 seconds
  • Attempt 6+: Poll every 30 seconds

Background Handling

  • App Active: Normal polling intervals
  • App Background: Polling paused, relies on push notifications
  • App Resumed: Polling resumes for any active requests

Offline Handling

The SDK provides robust offline support with automatic request queuing and retry mechanisms.

How Offline Handling Works

  1. Network Detection: Automatically detects when device goes offline
  2. Request Queuing: Queues requests in persistent storage when offline
  3. Automatic Retry: Processes queued requests when network is restored
  4. Battery Optimization: Uses longer polling intervals in background mode
  5. Push Fallback: Switches to push notifications when polling times out

Offline Configuration

const client = new MiltonAsyncClient({
  baseUrl: 'https://api.milton.com',
  apiKey: 'your-api-key',
  offlineConfig: {
    enableOfflineQueue: true,    // Enable offline request queuing
    maxQueueSize: 50,           // Maximum requests to queue
    retryAttempts: 3,           // Retry attempts for failed requests
    retryDelay: 5000,           // Delay between retries (ms)
    storageKey: 'milton_queue'  // AsyncStorage key for persistence
  }
});

Battery Optimization

const client = new MiltonAsyncClient({
  baseUrl: 'https://api.milton.com',
  apiKey: 'your-api-key',
  pollingConfig: {
    intervals: [1, 2, 4, 8, 15, 30],        // Foreground intervals
    backgroundIntervals: [30, 60, 120, 300], // Background intervals
    maxAttempts: 6,
    batteryOptimized: true                   // Enable battery optimization
  }
});

Offline Usage Example

// Submit request - works both online and offline
const response = await client.submitUserMessage({
  orgId: 123,
  userId: 456,
  question: "Log my lunch"
}, {
  onProgress: (status) => {
    if (status.status === 'queued_offline') {
      showMessage('Request queued - will process when online');
    } else if (status.status === 'waiting_for_push') {
      showMessage('Waiting for push notification...');
    }
  },
  onComplete: (result) => {
    showMessage('Request completed successfully');
  },
  onError: (error) => {
    if (error.message.includes('offline')) {
      showMessage('Request failed - check connection');
    }
  }
});

// Check offline queue status
const queueStatus = client.getOfflineQueueStatus();
console.log(`${queueStatus.queueLength} requests queued`);

Network State Handling

The SDK automatically handles network state changes:

// Network goes offline
// → Current requests switch to offline mode
// → New requests are queued automatically

// Network restored
// → Queued requests are processed automatically
// → Polling resumes for active requests
// → User receives completion notifications

Battery Optimization Features

Foreground Mode:

  • Normal polling intervals: 1s, 2s, 4s, 8s, 15s, 30s
  • Full polling attempts (6 attempts)
  • Immediate network retry on failure

Background Mode:

  • Extended intervals: 30s, 60s, 120s, 300s
  • Reduced polling attempts (4 attempts)
  • Switches to push notification mode faster
  • Pauses polling to save battery

Push Notification Fallback:

  • Automatically switches to push mode after 2 minutes of polling
  • Resumes polling when app becomes active
  • Provides seamless user experience

Advanced Usage

Custom Polling Configuration

const client = new MiltonAsyncClient({
  baseUrl: 'https://api.milton.com',
  apiKey: 'your-api-key',
  pollingConfig: {
    intervals: [2, 5, 10, 20], // Custom intervals in seconds
    maxAttempts: 4,
    timeoutMs: 60000
  }
});

Webhook Integration (Web Clients)

For web applications that can receive webhooks:

await client.submitUserMessage({
  orgId: 123,
  userId: 456,
  question: "Analyze my meal"
}, {
  webhookUrl: 'https://your-app.com/webhook/milton',
  clientId: 'web-client-123'
});

Error Handling

try {
  const response = await client.submitUserMessage(request, {
    onError: (error) => {
      if (error.message.includes('HTTP 503')) {
        // Server overloaded, retry later
        setTimeout(() => retryRequest(), 5000);
      } else {
        // Handle other errors
        showErrorMessage(error.message);
      }
    }
  });
} catch (error) {
  // Handle submission errors
  console.error('Failed to submit request:', error);
}

Firebase Cloud Messaging Setup

1. Create Firebase Project

  1. Go to Firebase Console
  2. Create a new project or use existing
  3. Add your iOS and Android apps

2. Download Configuration Files

  • iOS: Download GoogleService-Info.plist
  • Android: Download google-services.json

3. Configure React Native Firebase

Follow the official React Native Firebase setup guide.

4. Test Push Notifications

import messaging from '@react-native-firebase/messaging';

// Test FCM token generation
messaging().getToken().then(token => {
  console.log('FCM Token:', token);
});

// Test notification reception
messaging().onMessage(async remoteMessage => {
  console.log('FCM Message received:', remoteMessage);
});

Troubleshooting

Common Issues

Push notifications not working:

  • Verify Firebase configuration files are correctly added
  • Check that FCM token is being generated
  • Ensure push notification permissions are granted
  • Test with Firebase Console test message

Polling not stopping:

  • Check that push notification data includes request_id
  • Verify notification type is analysis_complete
  • Check console logs for push notification handling

Requests timing out:

  • Increase timeout in SDK config
  • Check network connectivity
  • Verify API endpoint URLs are correct

Debug Logging

Enable debug logging to troubleshoot issues:

// The SDK automatically logs to console
// Check React Native debugger or device logs for:
// - "Milton SDK: FCM Token obtained"
// - "Milton SDK: Received completion notification"
// - "Milton SDK: Polling error" or other error messages

Migration from Sync API

Before (Synchronous)

// Old synchronous approach
const response = await fetch('/user', {
  method: 'POST',
  body: JSON.stringify(request)
});
const result = await response.json(); // Waits 5-15 seconds

After (Asynchronous)

// New asynchronous approach
const response = await client.submitUserMessage(request, {
  onComplete: (result) => {
    // Handle result when ready (via push notification or polling)
    updateUI(result);
  }
});
// Returns immediately with request ID

License

MIT

Documentation

Complete Documentation Suite

Examples

Support

For issues and questions: