OddSockets React Native SDK

Official React Native SDK for OddSockets real-time messaging platform

npm ready React Native TypeScript High Performance Enhanced Surface

Overview & Features

The OddSockets React Native SDK provides a powerful, easy-to-use interface for real-time messaging in React Native applications, with full TypeScript support and React Native-specific optimizations.

React Native Optimized

Built specifically for React Native with proper Metro bundler support and native performance.

Full TypeScript

Complete TypeScript implementation with comprehensive type definitions and strict typing.

Enhanced Surface

Reactions, typing, threads, DMs, presence and more over the live socket.

High Performance

Optimized for mobile with efficient WebSocket connections and smart routing.

Cost Effective

No per-message pricing, industry-standard 32KB message limits, transparent pricing.

Automatic Failover

Built-in redundancy and intelligent error handling for 99.9% uptime.

Installation

bash
npm install oddsockets-react-native
bash
yarn add oddsockets-react-native
bash
expo install oddsockets-react-native

Quick Start

Basic Usage

typescript
import OddSockets from 'oddsockets-react-native';

const client = new OddSockets({
  apiKey: 'ak_live_1234567890abcdef',
  userId: 'user-123'
});

const channel = client.channel('my-channel');

// Subscribe to messages
await channel.subscribe((message) => {
  console.log('Received:', message);
});

// Publish a message
await channel.publish('Hello, React Native!');

React Component Example

typescript
import React, { useState, useEffect } from 'react';
import { View, Text, TextInput, TouchableOpacity } from 'react-native';
import OddSockets from 'oddsockets-react-native';

const ChatComponent = () => {
  const [client] = useState(() => new OddSockets({
    apiKey: 'ak_live_1234567890abcdef',
    userId: 'user-123'
  }));
  
  const [messages, setMessages] = useState([]);
  const [inputText, setInputText] = useState('');

  useEffect(() => {
    const channel = client.channel('chat');
    
    channel.subscribe((message) => {
      setMessages(prev => [...prev, message]);
    });

    return () => {
      client.disconnect();
    };
  }, []);

  const sendMessage = async () => {
    if (inputText.trim()) {
      const channel = client.channel('chat');
      await channel.publish(inputText);
      setInputText('');
    }
  };

  return (
    <View>
      {messages.map((msg, index) => (
        <Text key={index}>{msg.message}</Text>
      ))}
      <TextInput
        value={inputText}
        onChangeText={setInputText}
        placeholder="Type a message..."
      />
      <TouchableOpacity onPress={sendMessage}>
        <Text>Send</Text>
      </TouchableOpacity>
    </View>
  );
};

Enhanced Events

typescript
import OddSockets from 'oddsockets-react-native';

const client = new OddSockets({ apiKey: 'ak_live_1234567890abcdef', userId: 'alice' });
const channel = client.channel('room-42');
await channel.subscribe(() => {}, { enablePresence: true });

// Receive: broadcasts forwarded onto the client event surface
client.on('user_typing',    (data) => console.log(`${data.userId} is typing`));
client.on('reaction_added', (data) => console.log(`${data.userId} reacted ${data.emoji}`));

// Send: enhanced actions over the live socket
client.enhanced.startTyping('alice', 'room-42');
client.enhanced.addReaction({
  messageId: 'msg-1', channel: 'room-42', emoji: ':thumbsup:',
  userId: 'alice', userName: 'Alice',
});

Configuration

Client Options

typescript
const client = new OddSockets({
  apiKey: 'your-api-key',           // Required: Your OddSockets API key
  userId: 'user-id',                // Optional: User identifier
  autoConnect: true,                // Optional: Auto-connect on creation
  options: {                        // Optional: Socket.IO options
    timeout: 10000,
    transports: ['websocket', 'polling']
  }
});

Channel Options

typescript
channel.subscribe(callback, {
  enablePresence: true,             // Enable presence tracking
  retainHistory: true,              // Retain message history
  maxHistory: 100                   // Max history size
});

channel.publish(message, {
  ttl: 3600,                        // Time to live (seconds)
  metadata: { priority: 'high' },   // Additional metadata
  storeInHistory: true              // Store in message history
});

Examples

Explore comprehensive examples demonstrating the OddSockets React Native SDK in action:

Enhanced Features

Beyond core pub/sub, OddSockets ships a Slack-like enhanced surface — reactions, typing indicators, threads, read receipts, presence/status, notifications, DMs, channel management, message editing and search. It lives on client.enhanced. Send an action with a client.enhanced.* method; receive the paired broadcast with client.on('<event>', handler) — the worker forwards every enhanced broadcast onto the client event surface.

Typing & Reactions

typescript
const channel = client.channel('room-42');
await channel.subscribe(() => {}, { enablePresence: true });

// Receive-path: broadcasts from other users on the channel
client.on('user_typing',    (data) => console.log(`${data.userId} is typing`));
client.on('reaction_added', (data) => console.log(`${data.userId} reacted ${data.emoji}`));

// Send-path: enhanced actions over the live socket
client.enhanced.startTyping('alice', 'room-42');
client.enhanced.addReaction({
  messageId: 'msg-1', channel: 'room-42', emoji: ':thumbsup:',
  userId: 'alice', userName: 'Alice',
});

Threads

typescript
client.on('thread_reply', (data) => console.log('new thread reply', data));

const reply = await client.enhanced.threadReply({
  channel: 'room-42', parentMessageId: 'msg-1',
  message: 'Replying in the thread', userId: 'alice', userName: 'Alice',
});

// Query methods return a Promise that resolves with the worker response
const thread = await client.enhanced.getThread('thread-1');

Each area exposes methods on client.enhanced; the worker broadcasts the paired events which you handle with client.on(...):

  • TypingstartTyping, stopTypinguser_typing, user_stopped_typing
  • ReactionsaddReaction, removeReaction, getReactionsreaction_added, reaction_removed
  • ThreadsthreadReply, getThread, subscribeThread, followThread, markThreadReadthread_reply, thread_subscribed, thread_followed, thread_read_updated
  • Read receiptsmarkRead, markAllRead, getUnreadCountsuser_read, unread_count_updated, all_marked_read
  • MessageseditMessage, deleteMessage, pinMessage, unpinMessage, getPinnedMessagesmessage_edited, message_deleted, message_pinned, message_unpinned
  • Presence & statussetStatus, setCustomStatus, setDND, getUserPresenceuser_status_changed, custom_status_updated, dnd_status_changed
  • ChannelscreateChannel, updateChannel, archiveChannel, inviteToChannel, joinChannel, leaveChannelchannel_created, channel_updated, user_invited, user_joined_channel, user_left_channel
  • DMscreateDM, sendDM, getDMConversationsdm_created, dm_received
  • NotificationssubscribeNotifications, getNotifications, markNotificationRead, clearNotificationsnotification, notification_read, notifications_cleared
  • SearchsearchMessages, searchInChannel, searchByUser, filterMessages (results returned via Promise)

For any worker event not wrapped above, subscribe with the raw client.on('<event>', handler) API — all enhanced broadcasts are forwarded onto the client surface.

Challenges & Leaderboards

Challenges, leaderboards and achievements build on the same live socket. The send side lives on the enhanced surface (client.enhanced.*); request/query methods resolve with the worker's reply (use await), while progress and achievement calls are fire-and-forget. Inbound broadcasts arrive on the client event surface — subscribe with the client's normal client.on('<event>', handler).

typescript
// Create a ranked challenge (leaderboard-backed)
await client.enhanced.createChallenge({ challengeId: 'daily-sprint', metric: 'points', ranked: true });

// Report progress as it happens (fire-and-forget)
client.enhanced.reportProgress({ challengeId: 'daily-sprint', value: 250 });

// Request the current standings (top-N + caller rank)
const standings = await client.enhanced.getStandings({ challengeId: 'daily-sprint', limit: 10 });

// Finalize with an outcome
await client.enhanced.completeChallenge({ challengeId: 'daily-sprint', outcome: 'completed' });

All methods live on client.enhanced. Request/query methods return a Promise that resolves with the worker acknowledgement; progress and achievement calls are fire-and-forget (no ack):

  • createChallenge — create a challenge / leaderboard. Ack challenge_create_success.
  • reportProgress — fire-and-forget metric progress (no ack).
  • completeChallenge — finalize with an outcome. Ack challenge_complete_success.
  • unlockAchievement — fire-and-forget; pass percentComplete (0–100) (no ack).
  • getStandings — request top-N + caller rank. Ack challenge_standings_success.
  • getAchievements — query achievement state. Ack achievement_state.
  • sendChallengeInvite — directed invite to another user. Ack challenge_invite_success.
  • replyChallengeInvite — accept/decline an invite. Ack challenge_reply_success.
  • cancelChallengeInvite — cancel a sent invite. Ack challenge_invite_cancel_success.
  • getChallengeInvites — list pending invites. Ack challenge_invites.

Outcome vocabulary

The outcome passed to completeChallenge is one of:

  • completed — win (rank 1)
  • failed — loss
  • tied — draw
  • conceded — resign / concede
  • expired — timed out

Progressive achievements

unlockAchievement always emits the wire event achievement_unlock; the worker is authoritative and derives the outbound broadcast from percentComplete. A value < 100 broadcasts achievement_progress (status in_progress); a value >= 100 or omitted broadcasts achievement_unlock (status unlocked). You never emit achievement_progress yourself.

Inbound events

Subscribe to broadcasts and directed events with client.on('<event>', handler):

  • Room broadcastschallenge_progress, leaderboard_rank_change, challenge_complete, achievement_unlock, achievement_progress
  • Directed (per-user)challenge_invited, challenge_reply_received, challenge_invite_cancelled

Usage Analytics

Pull your tenant's headline usage tiles — monthly active users, daily active users, total messages published, and error rate — straight from the SDK, without hand-rolling a REST call. getUsageStats() resolves the same four tiles the developer dashboard renders.

const stats = await client.getUsageStats();

// Each tile is number | null: null means "not live yet", never a real zero.
const dash = (v: number | null) => v ?? '\u2014';
console.log(`MAU:      ${dash(stats.mau)}`);
console.log(`DAU:      ${dash(stats.dau)}`);
console.log(`Messages: ${dash(stats.totalMessages)}`);
console.log(`Errors:   ${dash(stats.errorRate)}`); // em-dash when null

The four tiles

  • mau — monthly active users for your owner scope
  • dau — daily active users
  • totalMessages — total messages published
  • errorRate — publish error rate, 01

Honesty rule — null, never a fake zero

Each tile is a number or null. A null means that leg of the analytics pipeline is not live yet for your tenant — the SDK returns null verbatim and never coerces it to 0. Render an em-dash () for a null tile so you never show a fabricated zero.

Requires an API key

getUsageStats() reads your owner-scoped analytics, so it needs an apiKey. Keyless / token-only clients have no owner scope to query and will throw getUsageStats requires an apiKey.

Performance & Compatibility

OddSockets React Native SDK delivers superior performance with broad compatibility:

<50ms
Latency
99.9%
Uptime
32KB
Max Message
1M+
Messages/sec

React Native Support

  • React Native 0.68+ (2022)
  • iOS 11+ / Android API 21+
  • Expo SDK 46+
  • Metro bundler compatible

TypeScript Support

  • TypeScript 4.5+
  • Full type definitions
  • Strict mode compatible
  • IntelliSense support

React Native Specific Features

The OddSockets React Native SDK includes several React Native-specific optimizations and features:

Mobile Optimized

Optimized for mobile networks with intelligent reconnection and battery-efficient polling.

Metro Compatible

Fully compatible with Metro bundler and React Native's module resolution system.

Fetch API

Uses React Native's built-in fetch API instead of external HTTP libraries for better compatibility.

Background Support

Handles app backgrounding gracefully with automatic reconnection when returning to foreground.

React Hooks Integration

typescript
// Custom hook for OddSockets
const useOddSockets = (apiKey: string, userId: string) => {
  const [client] = useState(() => new OddSockets({ apiKey, userId }));
  const [isConnected, setIsConnected] = useState(false);

  useEffect(() => {
    client.on('connected', () => setIsConnected(true));
    client.on('disconnected', () => setIsConnected(false));

    return () => {
      client.disconnect();
    };
  }, [client]);

  return { client, isConnected };
};

// Usage in component
const MyComponent = () => {
  const { client, isConnected } = useOddSockets('your-api-key', 'user-123');
  
  return (
    <View>
      <Text>Status: {isConnected ? 'Connected' : 'Disconnected'}</Text>
    </View>
  );
};