Code Monkey home page Code Monkey logo

pusher-websocket-react-native's Introduction

Pusher React Native Websocket Client

Twitter GitHub license npm version

This is the Pusher Channels React Native client.

For tutorials and more in-depth information about Pusher Channels, visit our official docs.

Supported Mobile platforms

Deployment targets

  • iOS 13.0 and above
  • Android 7 and above. Android 6 will require desugaring.

Example Application

By cloning this repository you can check the React Native example application, a minimal application to connect to a channel and send events.

Table of Contents

Installation

To integrate the plugin in your React Native App, you need to add the plugin to your package.json:

npm install @pusher/pusher-websocket-react-native

or

yarn add @pusher/pusher-websocket-react-native

iOS specific installation

The Pusher Channels React Native plugin adds the pusher-websocket-swift cocoapod to your project. You probably need to run a

$ pod install

in the ios directory.

Android specific installation

Gradle should automatically include the pusher-websocket-java dependency.

Initialization

The Pusher class is a singleton that can be instantiated with getInstance(). Then you need to initialize the client with several configuration options. Here is a quick example with several callbacks options:

import {
  Pusher,
  PusherMember,
  PusherChannel,
  PusherEvent,
} from '@pusher/pusher-websocket-react-native';

const pusher = Pusher.getInstance();

try {
  await pusher.init({
    apiKey: APP_KEY,
    cluster: APP_CLUSTER,
    // authEndpoint: '<YOUR ENDPOINT URI>',
    onConnectionStateChange,
    onError,
    onEvent,
    onSubscriptionSucceeded,
    onSubscriptionError,
    onDecryptionFailure,
    onMemberAdded,
    onMemberRemoved,
    onSubscriptionCount,
  });

  await pusher.subscribe({ channelName });
  await pusher.connect();
} catch (e) {
  console.log(`ERROR: ${e}`);
}

After calling init(...) you can connect to the Pusher servers. You can subscribe to channels before calling connect().

Configuration

There are a few configuration parameters which can be set for the Pusher client. The following table describes available parameters for each platform:

parameter Android iOS
activityTimeout
apiKey
authEndpoint
cluster
maxReconnectGapInSeconds
maxReconnectionAttempts
pongTimeout
proxy ⬜️
useTLS
authorizerTimeoutInSeconds ⬜️

activityTimeout (double)

If no messages are received after this time period (in seconds), the ping message is sent to check if the connection is still working. The server supplies the default value, low values result in unnecessary traffic.

apiKey (string)

You can get your APP_KEY and APP_CLUSTER from the the App page on the App Keys section in your Pusher Channels Dashboard

authEndpoint (string)

The authEndpoint provides a URL that the Pusher client will call to authorize users for a presence channel. Learn how to implement an authorization service

cluster (string)

Specifies the cluster that pusher-js should connect to. Here's the full list of Pusher clusters. If you do not specify a cluster, mt1 will be used by default.

useTLS (bool)

Whether or not you would like to use TLS encrypted transport or not, default is true.

authorizerTimeoutInSeconds (double)

If onAuthorizer callback is not called in Javascript before this time period (in seconds), the authorization for the channel will timeout on the native side. Default value: 10 seconds. iOS only.

Event Callback parameters

The following functions are callbacks that can be passed to the init() method. All are optional.

onEvent

function onEvent(PusherEvent event) {
  console.log(`onEvent: ${event}`);
}

Called when an event is received by the client. The global event handler will trigger events from any channel.

onSubscriptionSucceeded

function onSubscriptionSucceeded(channelName:string, data:any) {
  console.log(`onSubscriptionSucceeded: ${channelName} data: ${data}`);
}

Use this if you want to be informed when a channel has successfully been subscribed to. This is useful if you want to perform actions that are only relevant after a subscription has succeeded. For example, querying the members for presence channel.

onSubscriptionError

function onSubscriptionError(channelName: string, message:string, e:any) {
  console.log(`onSubscriptionError: ${message} channelName: ${channelName} Exception: ${e}`);
}

Use this if you want to be informed of a failed subscription attempt. You can use this to re-attempt the subscription or make a call to a service you use to track errors.

onDecryptionFailure

function onDecryptionFailure(event:string, string reason:string) {
  console.log(`onDecryptionFailure: ${event} reason: ${reason}`);
}

Used with private channels only. Use this if you want to be notified if any messages fail to decrypt.

onSubscriptionCount

function onSubscriptionCount(subscriptionCount:number) {
  console.log(`onSubscriptionSucceeded: ${subscriptionCount}`);
}

is an event that can be manually enabled on the server to count the number of connections that are currently subscribed to a particular channel. They work with all channel types, except presence channels. See Counting live users at scale with subscription_count events for more information.

onMemberAdded

function onMemberAdded(channelName:string, member:PusherMember) {
  console.log(`onMemberAdded: ${channelName} member: ${member}`);
}

Called when a member is added to the presence channel.

onMemberRemoved

function onMemberRemoved(channelName:string, member:PusherMember) {
  console.log(`onMemberRemoved: ${channelName} member: ${member}`);
}

Called when a member is removed from the presence channel.

onAuthorizer

When passing the onAuthorizer() callback to the init() method, this callback is called to request auth information. Learn how to generate the correct auth signatures

async function onAuthorizer(channelName:string, socketId:string):Promise<PusherAuthorizerResult> {
  return {
    auth: "foo:bar",
    channel_data: '{"user_id": 1}',
    shared_secret: "foobar"
  };
}

onConnectionStateChange

function onConnectionStateChange(currentState:string, previousState:string) {
  console.log(`Connection: ${currentState}`);
}

Use this if you want to use connection state changes to perform different actions/UI updates. The connection can have different states, as follows:

  • CONNECTING - Currently attempting to establish a connection
  • CONNECTED - Connection successfully established
  • DISCONNECTING - Connection is about to be disconnected.
  • DISCONNECTED - Connection has been disconnected with no attempts to automatically reconnect.
  • RECONNECTING - Atempting to re-establish the connection.

onError

function onError(message:string, code:int, e:any) {
  console.log(`onError: $message code: ${code} exception: ${e}`);
}

Use this if you want to be informed about errors received from Pusher Channels. For example, Application is over connection quota. For more details, refer to Pusher error codes.

Connection handling

Connecting

To connect to the Pusher network, call the connect() method.

await pusher.connect();

Disconnecting

To disconnect from the Pusher network, just call the disconnect() method.

await pusher.disconnect();

Reconnection

There are three main ways why a connection could be disconnected:

  • The client explicitly calls disconnect and a close frame is sent over the websocket connection.
  • The client experiences some form of network degradation which leads to a heartbeat (ping/pong) message being missed and thus the client disconnects.
  • The Pusher server closes the websocket connection; typically this will only occur during a restart of the Pusher socket servers and almost immediatelly it will reconnect.

In the case of the first type of disconnection, the library will (as you would hope) not attempt to reconnect.

Subscribing

Public channels

The default method for subscribing to a channel involves invoking the subscribe method of your client object:

const myChannel = await pusher.subscribe({channelName: "my-channel"});

Private channels

Private channels are created in exactly the same way as public channels, except that they reside in the 'private-' namespace. This means prefixing the channel name:

const myPrivateChannel = await pusher.subscribe({channelName: "private-my-channel"})

To subscribe to a private channel, the client needs to be authenticated. Refer to the Configuration section for the authenticated channel example.

Private encrypted channels

Similar to Private channels, you can also subscribe to a private encrypted channel. This library now fully supports end-to-end encryption. This means that only you and your connected clients will be able to read your messages. Pusher cannot decrypt them.

Like with private channels, you must provide an authentication endpoint. That endpoint must be using a server client that supports end-to-end encryption. There is a demonstration endpoint to look at using nodejs.

The shared secret used to decrypt events is loaded from the same auth endpoint request that is used to authorize your subscription. There is also a mechanism for reloading the shared secret if your encryption master key changes. If an event is encountered that cannot be decrypted, a request is made to your auth endpoint to attempt to load the new shared secret. If that request fails or if the returned secret still cannot decrypt the event then that event will be skipped, the onDecryptionFailure callback function will be called, and the next received event will be processed.

Limitations

  • Client events are not supported on encrypted channels
const privateEncryptedChannel = await pusher.subscribe({channelName: "private-encrypted-my-channel"})

There is also an optional callback in the connection delegate when you can listen for any failed decryption events:

void onDecryptionFailure(event:string, reason:string)

Presence channels

Presence channels are channels whose names are prefixed by presence-.

The resulting channel object has a member: members that contains the active members of the channel.

const myPresenceChannel = await pusher.subscribe({channelName: "presence-my-channel"})

You can also provide functions that will be called when members are either added to or removed from the channel. These are available as parameters to init() globally, or to subscribe() per channel.

void onMemberAdded(channelName:string, member:PusherMember) {
  console.log(`onMemberAdded: ${channelName} user: ${member}`);
}
void onMemberRemoved(channelName:string, member:PusherMember) {
  console.log(`onMemberRemoved: ${channelName} user: ${member}`);
}

Note: The members property of PusherChannel objects will only be set once subscription to the channel has succeeded.

The easiest way to find out when a channel has been successfully subscribed to is to bind to the callback named onSubscriptionSucceeded on the channel you're interested in. It would look something like this:

const pusher = Pusher.getInstance();

await pusher.init({
  apiKey: API_KEY,
  cluster: API_CLUSTER,
  authEndPoint: "https://your-server.com/pusher/auth"
});
const myChannel = await pusher.subscribe(
  channelName:'presence-my-channel',
  onSubscriptionSucceeded: (channelName, data) => {
    console.log(`Subscribed to ${channelName}`);
    console.log(`I can now access me: ${myChannel.me}`)
    console.log(`And here are the channel members: ${myChannel.members}`)
  },
  onMemberAdded: (member) => {
    console.log(`Member added: ${member}`);
  },
  onMemberRemoved: (member) => {
    console.log(`Member removed: ${member}`);
  },
  onEvent: (event) => {
    console.log(`Event received: ${event}`);
  },
);

Note that both private and presence channels require the user to be authenticated to subscribe to the channel. This authentication can either happen inside the library if you configured your Pusher object with your app's secret, or an authentication request is made to an authentication endpoint that you provide, again when initializing your Pusher object.

We recommend that you use an authentication endpoint over including your app's secret in your app in the vast majority of use cases. If you are completely certain that there's no risk to you including your app's secret in your app. For example, if your app is just for internal use at your company, then it can make things easier than setting up an authentication endpoint.

Unsubscribing

To unsubscribe from a channel, call the unsubscribe() method:

await pusher.unsubscribe({channelName:"my-channel"});

Binding to events

Events can be bound to at two levels; globally and per channel. There is an example of this below.

Per-channel events

These are bound to a specific channel. You can reuse event names in different parts of your client application.

const pusher = Pusher.getInstance();
await pusher.init({
  apiKey: API_KEY,
  cluster: API_CLUSTER
});
const myChannel = await pusher.subscribe({
  channelName: "my-channel",
  onEvent: (event) => {
    console.log(`Got channel event: ${event}`);
  }
});
await pusher.connect();

Global events

You can attach behavior to these events regardless of the channel the event is broadcast to.

const pusher = Pusher.getInstance();
await pusher.init({
  apiKey: API_KEY,
  cluster: API_CLUSTER,
  onEvent: (event) {
    console.log(`Got event: ${event}`);
  }
});
const myChannel = await pusher.subscribe({
  channelName: "my-channel"
});

PusherEvent

The callbacks you bind receive a PusherEvent:

class PusherEvent {
  channelName:string; // Name of the channel.
  eventName:string; // Name of the event.
  data:any; // Data, usually JSON string. See [parsing event data](#parsing-event-data).
  userId:string; // UserId of the sending event, only for client events on presence channels.
}

Parsing event data

The data property of PusherEvent contains the string representation of the data that you passed when you triggered the event. If you passed an object then that object will have been serialized to JSON. You can parse that JSON as appropriate.

Receiving errors

Errors received from Pusher Channels can be accessed via the onError callback.

void onError(message:string, code:int, e:any) {
  console.log(`onError: ${message} code: ${code} exception: ${e}`);
}

Triggering events

Once a private or presence subscription has been authorized (see authenticating users) and the subscription has succeeded, it is possible to trigger events on those channels. You can do this per channel, or on the global Pusher instance.

await myChannel.trigger({eventName: "client-my-event", data: {"myName": "Bob"}});

Or on the global pusher instance:

await pusher.trigger({channelName: "my-channel", eventName: "client-my-event", data: {"myName": "Bob"}});

Events triggered by clients are called client events. Because they are being triggered from a client which may not be trusted, there are a number of enforced rules when using them. Some of these rules include:

  • Event names must have a client- prefix
  • Rate limits
  • You can only trigger an event when the subscription has succeeded

For more details, refer to client events.

Get a channel by name

To get the PusherChannel instance from the Pusher instance you can use the getChannel(<channelName>) method:

const channel = pusher.getChannel("presence-channel");

Socket information

To get information from the current socket call the getSocketId() method:

const socketId = await pusher.getSocketId();

Communication

  • If you have found a bug, please open an issue.
  • If you have a feature request, open an issue.
  • If you want to contribute, submit a pull request.

Credits

Pusher is owned and maintained by Pusher.

License

Pusher is released under the MIT license. Refer to LICENSE for more details.

pusher-websocket-react-native's People

Contributors

benjamin-tang-pusher avatar benw-pusher avatar callaars avatar daansystems avatar ekrembk avatar essaji avatar evrimfeyyaz avatar fbenevides avatar messagebtilly avatar proggen-com avatar pusher-ci avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pusher-websocket-react-native's Issues

App crashing after reload

When reloading the app with CMD + D, this is logged to the console: Invalidating RCTSampleTurboModule...

Then, if I receive an event, it's crashing with the following error:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Error when sending event: onEvent with body: {
channelName = shows;
data = "[]";
eventName = "App\\Events\\RefreshShows";
userId = "<null>";
}. RCTCallableJSModules is not set. This is probably because you've explicitly synthesized the RCTCallableJSModules in PusherWebsocketReactNative, even though it's inherited from RCTEventEmitter.'
*** First throw call stack:
(
0   CoreFoundation                      0x00000001803f3d70 __exceptionPreprocess + 236
1   libobjc.A.dylib                     0x000000018019814c objc_exception_throw + 56
2   Foundation                          0x000000018075b6c0 -[NSMutableDictionary(NSMutableDictionary) classForCoder] + 0
3   projectname                               0x0000000100978000 -[RCTEventEmitter sendEventWithName:body:] + 400
4   projectname                               0x0000000100ca81fc $s29pusher_websocket_react_native26PusherWebsocketReactNativeC8callback4nam<…>

Anyone encountered this issue?

Expo: 46.0.16
React Native: 0.69.6
pusher-websocket-react-native: 1.1.0

Failed to build with Android

When running yarn react-native run-android it failed with the following error.

React Native Version: 0.69.2
pusher-websocket-react-native version: 1.0.2.
Bare react-native project

> Task :pusher_pusher-websocket-react-native:compileDebugKotlin FAILED Deprecated Gradle features were used in this build, making it incompatible with Gradle 8.0.

pod install also fails

[!] CocoaPods could not find compatible versions for pod "pusher-websocket-react-native": In Podfile: pusher-websocket-react-native (from ../node_modules/@pusher/pusher-websocket-react-native`)

Specs satisfying the pusher-websocket-react-native (from ../node_modules/@pusher/pusher-websocket-react-native) dependency were found, but they required a higher minimum deployment target.`

Async functions should be sync

What is the issue?

There are many libraries like laravel-echo which expect sync function eg:

this.subscription = this.pusher.subscribe(this.name);

but this library expects everything to have a await

...

Is it a crash report? Submit stack traces or anything that you think would help

...

No

Any improvements you suggest

...

Sure, the api should match the pusher-js so everything would work everywhere!

I don't even understand why pusher-js is deprecated for react-native while clearly this is not an alternative to that, why would anybody think this is a better solution, let alone the fact that it support only iOS 13 above and Android 7 above, it's a nightmare to install and after you do finally install it, you realize it is different and doesn't even work!


CC @pusher/mobile

Execution failed for task ':@pusher_pusher-websocket-react-native:generateReleaseRFile'.

> Task :@pusher_pusher-websocket-react-native:generateReleaseRFile FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':@pusher_pusher-websocket-react-native:generateReleaseRFile'.
> Could not resolve all files for configuration ':@pusher_pusher-websocket-react-native:releaseCompileClasspath'.
   > Failed to transform react-native-0.71.0-rc.0-release.aar (com.facebook.react:react-native:0.71.0-rc.0) to match attributes {artifactType=android-symbol-with-package-name, com.android.build.api.attributes.BuildTypeAttr=release, org.gradle.category=library, org.gradle.dependency.bundling=external, org.gradle.libraryelements=aar, org.gradle.status=release, org.gradle.usage=java-api}.
      > Could not find react-native-0.71.0-rc.0-release.aar (com.facebook.react:react-native:0.71.0-rc.0).
        Searched in the following locations:
            https://repo.maven.apache.org/maven2/com/facebook/react/react-native/0.71.0-rc.0/react-native-0.71.0-rc.0-release.aar

how to solve this problem?

Connect to a different host

Hi there,

Is is possible to connect to a server other than Pusher? So is it possible to pass along something like host: '127.0.0.1:6001?

App closes without logs Android/IOS

i need help on my project. my app closes without any error.

but when i go into device logs/adb crash logs i get error from pusher.

Version:
react-native: "0.69.7",
@pusher/pusher-websocket-react-native: ^1.1.1,

example:IOS logs
`
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Triggered by Thread: 0

Last Exception Backtrace:
0 CoreFoundation 0x7fff204055f4 __exceptionPreprocess + 226
1 libobjc.A.dylib 0x7fff201a4a45 objc_exception_throw + 48
2 Foundation 0x7fff2075ea19 -[NSMutableDictionary(NSMutableDictionary) classForCoder] + 0
3 React 0x108bd5d7a -[RCTEventEmitter sendEventWithName:body:] + 442 (RCTEventEmitter.m:50)
4 pusher_websocket_react_native 0x107b1c481 PusherWebsocketReactNative.callback(name:body:) + 273 (PusherWebsocketReactNative.swift:27)
5 pusher_websocket_react_native 0x107b20266 PusherWebsocketReactNative.changedConnectionState(from:to:) + 374 (PusherWebsocketReactNative.swift:119)
6 pusher_websocket_react_native 0x107b202bf @objc PusherWebsocketReactNative.changedConnectionState(from:to:) + 47
7 PusherSwift 0x10768f92a partial apply + 26
8 PusherSwift 0x10768ae96 PusherConnection.updateConnectionState(to:) + 390 (PusherConnection.swift:331)
9 PusherSwift 0x10768b0e2 PusherConnection.resetConnection() + 178 (PusherConnection.swift:349)
10 PusherSwift 0x10769913d PusherConnection.webSocketDidDisconnect(connection:closeCode:reason:) + 205 (PusherConnection+WebsocketDelegate.swift:53)
11 PusherSwift 0x10769b429 protocol witness for WebSocketConnectionDelegate.webSocketDidDisconnect(connection:closeCode:reason:) in conformance PusherConnection + 9
12 NWWebSocket 0x1070904d0 closure #1 in NWWebSocket.scheduleDisconnectionReporting(closeCode:reason:) + 288 (NWWebSocket.swift:318)
13 NWWebSocket 0x107090518 thunk for @escaping @callee_guaranteed () -> () + 40
14 libdispatch.dylib 0x7fff201237f4 _dispatch_block_async_invoke2 + 83
15 libdispatch.dylib 0x7fff20115b25 _dispatch_client_callout + 8
16 libdispatch.dylib 0x7fff20123043 _dispatch_main_queue_drain + 1050
17 libdispatch.dylib 0x7fff20122c1b _dispatch_main_queue_callback_4CF + 31
`

Crashes app when receiving an event ONLY if the remote JS debugger is on.

My Setup

"@expo/react-native-action-sheet": "^4.0.1",
"expo": "^47.0.0",

I'm using eas build with development build. From my understanding the eas build runs pod install during that process. My app is not yet ejected. I'm running the build on a device as opposed to the simulator.

I've got everything set up and working, with the onEvent listener triggering correctly.

The Problem

However a new problem appeared - the app just crashes when receiving an event when the JS debugger is running.

The crash happens even when no onEvent listener is defined.

EDIT: It also seems to happen sometimes when the debugger is closed

Anyone else experiences this?

com.pusher.client.Pusher.<init>

Exception java.lang.RuntimeException:
at com.facebook.react.bridge.JavaMethodWrapper.invoke (JavaMethodWrapper.java:382)
at com.facebook.react.bridge.JavaModuleWrapper.invoke (JavaModuleWrapper.java:150)
at com.facebook.react.bridge.queue.NativeRunnable.run
at android.os.Handler.handleCallback (Handler.java:739)
at android.os.Handler.dispatchMessage (Handler.java:95)
at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage (MessageQueueThreadHandler.java:26)
at android.os.Looper.loop (Looper.java:148)
at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run (MessageQueueThreadImpl.java:225)
at java.lang.Thread.run (Thread.java:818)
Caused by java.lang.reflect.InvocationTargetException:
at java.lang.reflect.Method.invoke
at com.facebook.react.bridge.JavaMethodWrapper.invoke (JavaMethodWrapper.java:371)
Caused by java.lang.NoClassDefFoundError:
at com.pusher.client.Pusher. (Pusher.java:103)
at com.pusher.client.Pusher. (Pusher.java:85)
at com.pusherwebsocketreactnative.PusherWebsocketReactNativeModule.initialize (PusherWebsocketReactNativeModule.kt:85)

Pusher Channels already initialized

hello
I want to use pusher-websocket-react-native in my react native project

my project is Quiz-english word game and I want to use pusher to create game between two player

first i create presence channel and this my code on react native and server ,

this is my server code


import { pusher } from "../../../util/lib";
import { user } from "../../../util/user";

export default async function handler(req, res) {
  const { socket_id, channel_name } = req.body;

  const randomString = Math.random().toString(36).slice(2);

  const presenceData = {
    user_id: randomString,
    user_info: { name: `user ${randomString}` },
  };

  

  try {
    const authResponse = pusher.authorizeChannel(
      socket_id,
      channel_name,
      presenceData
    );
    res.send(authResponse);
  } catch (err) {
    console.error(err);
  }
}

this is my react native code :

import React, {Component, useEffect, useState, user} from 'react';
import {
  ImageBackground,
  StyleSheet,
  Text,
  View,
  Image,
  ActivityIndicator,
  Dimensions,
  TouchableOpacity,
  BackHandler,
} from 'react-native';
import {AnimatedCircularProgress} from 'react-native-circular-progress';
import ParentConnect from '../../HOC/Container';
import {
  Pusher,
  PusherMember,
  PusherChannel,
  PusherEvent,
} from '@pusher/pusher-websocket-react-native';

const ChoosePlayerScreen = (props) => {
  const [members, onChangeMembers] = React.useState([]);

  const pusher = Pusher.getInstance();

  const connect = () => {
    //create pusher connector
    try {
      pusher.init({
        apiKey: '27ba431a698b22a56e47',
        cluster: 'mt1',
        authEndpoint: 'http://10.0.3.2:3000/api/auth/pusher',
        onSubscriptionSucceeded,
        onEvent,
      });
      pusher.connect();
      pusher.subscribe({
        channelName: 'presence-gamestart',
      });
    } catch (error) {}
  };

  const sendmsg = async (msg) => {
    // await pusher.trigger('zabanbazi', 'my-event', 'hello-client');
  };

  const onEvent = (event) => {
    console.log(`onEvent: ${event}`);
  };

  const onSubscriptionSucceeded = (channelName, data) => {
    console.log(
      `onSubscriptionSucceeded: ${channelName} data: ${JSON.stringify(data)}`,
    );
    const channel = pusher.getChannel(channelName);
    const me = channel.me;
    onChangeMembers([...channel.members.values()]);
    console.log(`Me: ${me}`);
  };

  useEffect(() => {
    // props.makeonline(true);

    connect();
    return () => {
      pusher.unsubscribe({channelName: 'presence-gamestart'});
    };
  }, []);

  return (
    <View style={{flex: 1}}>
      <ImageBackground
        source={require('../../assets/Picture/backChoose.png')}
        style={{
          height: Dimensions.get('window').height,
          width: Dimensions.get('window').width,
        }}>
        <View
          style={{
            width: '34%',
            alignSelf: 'center',
            backgroundColor: '#e6c529',
            padding: 8,
            borderRadius: 80,
            position: 'absolute',
            top: '7%',
          }}>
          <View></View>
          <AnimatedCircularProgress
            size={120}
            width={10}
            fill={0}
            rotation={360}
            style={{
              backgroundColor: '#f6e077',
              borderRadius: 80,
              alignSelf: 'center',
            }}
            tintColor="#c14747"
            backgroundColor="white">
            {(fill) => (
              <Text
                style={{
                  fontFamily: 'tahoma',
                  fontSize: 40,
                  color: 'white',
                  textAlign: 'center',
                  alignSelf: 'center',
                  fontWeight: 'bold',
                }}>
                0
              </Text>
            )}
          </AnimatedCircularProgress>
        </View>
        <View
          style={{
            width: '100%',
            flexDirection: 'column',
            bottom: 55,
            position: 'absolute',
            alignSelf: 'center',
            alignItems: 'center',
            left: 15,
          }}>
          <View style={{flexDirection: 'row'}}>
            <TouchableOpacity
              onPress={() => props.navigation.navigate('SearchPlayer')}
              style={{flex: 1, alignItems: 'flex-end'}}>
              <View style={{flex: 1, alignItems: 'flex-end'}}>
                <Image
                  style={{height: 150, width: 150}}
                  source={require('../../assets/Picture/SearchName.png')}
                />
              </View>
            </TouchableOpacity>
            <TouchableOpacity onPress={() => sendmsg()} style={{flex: 1}}>
              <View style={{flex: 1}}>
                <Image
                  style={{height: 150, width: 150}}
                  source={require('../../assets/Picture/ComputerPlayer.png')}
                />
              </View>
            </TouchableOpacity>
          </View>
          <View>
            <TouchableOpacity
              onPress={() => props.navigation.navigate('Invite')}
              style={{alignItems: 'center'}}>
              <View style={{alignItems: 'center'}}>
                <Image
                  style={{height: 150, width: 250}}
                  source={require('../../assets/Picture/invite.png')}
                />
              </View>
            </TouchableOpacity>
          </View>
        </View>
      </ImageBackground>
    </View>
  );
};

export default ParentConnect(ChoosePlayerScreen);

const styles = StyleSheet.create({});

when i return back to game page I see this error

E:\Project\zabanbazi\app\ZabanBazi\node_modules\react-devtools-core\dist\backend.js:32 Possible Unhandled Promise Rejection (id: 0):
Error: Pusher Channels already initialized.
Error: Pusher Channels already initialized.
    at Object.promiseMethodWrapper [as initialize] (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:2246:36)
    at Pusher.init (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:164128:43)
    at connect (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:221546:16)
    at http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:221585:7
    at commitHookEffectListMount (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:19632:32)
    at commitPassiveHookEffects (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:19667:19)
    at Object.invokeGuardedCallbackImpl (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:9188:16)
    at invokeGuardedCallback (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:9282:37)
    at flushPassiveEffectsImpl (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:22011:15)
    at unstable_runWithPriority (http://localhost:8081/index.bundle?platform=android&dev=true&minify=false:47225:18)

Receiving data multiple times

Listen to Multiple Events on a Pusher Channel
like

const createOrderChannel = async name => {
  await pusher.subscribe({
    channelName: name,
    onEvent: async event => {
      console.log(
        `################### Pusher ==> onEvent: ${
          JSON.parse(event.data).order
        }`,
      );
    },
  });
};

Cannot unsubscribe to pusher channel.

I've used this package in my react-native app but I'm unable to unsubscribe to the pusher channel.
I tried so many things. I searched for it on StackOverflow and other platforms but didn't get anything.

Here is the code of my screen.

import { Pusher } from '@pusher/pusher-websocket-react-native';

const pusher = Pusher.getInstance();

useEffect(() => {
     pusher.init({
            apiKey: process.env.API_KEY,
            cluster: process.env.CLUSTER
        });

      pusher.connect();

     pusher.subscribe({
                channelName: process.env.PUSHER_CHANNEL,
                onSubscriptionSucceeded: () => {
                    console.log("Pusher Subscription Added!");
                },
                onSubscriptionError: () => {
                    console.log("Error while Subscribing Channel!");
                },
                onEvent: (data) => {
                    let response = JSON.parse(data.data).results[0];

                    console.log(response);
                }
            });

        return () => {
              pusher.unsubscribe({channelName: process.env.PUSHER_CHANNEL});
              pusher.disconnect();
        }
},[]);

When I return to this screen, It will log previous subscriptions to the console. (This code got executed too many times.)

App crashes when re-subscribing to presence channel using Authorizer

Our use case has users connecting to pusher and subscribing to a presence channel using a custom Authorizer (needed in order to pass appropriate headers to our backend that can't be set by the authEndpoint). This works the first time through, but if the user leaves the screen (disconnects and unsubscribes) and comes back (reconnects and subscribes to the same channel) or even tries to join a different channel, the native module crashes the app. It would appear that calling init a second time possibly doesn't remap the new socket through the library or doesn't clear the old authorizer that depends on the socketID as part of the mutex key (crashes here:

)

There's no safety in this mutex accessor here - it explicitly unwraps an optional without checking it.

Trigger event without authorization

I'm doing a application without backend. I want to send messages in real-time with pusher but i have the following error: Trigger event is only for private/presence channels
Is it possible to do tirgger event without authorization?

Failing to launch app react native [android] after installing library

Problem:
Failing to launch app after installing react native sdk for pusher

Platforms:
React Native: 0.70.1
@pusher/pusher-websocket-react-native: "^1.0.2"

N.B: Only tried running Android

Steps to reproduce:

  1. Run yarn add @pusher/pusher-websocket-react-native as per documentation to install the pusher library.
  2. Run yarn android to launch app on device

Expectation:
App should launch

Actual:
App fails to launch with error:

On VSCode Terminal:

FAILURE: Build completed with 2 failures.

1: Task failed with an exception.
-----------
* What went wrong:
Execution failed for task ':pusher_pusher-websocket-react-native:compileDebugKotlin'.
> Compilation error. See log for more details

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
==============================================================================

On Android Studio:

	•	failed
	•	:pusher_pusher-websocket-react-native:compileDebugKotlin
	•	/Users/me/.gradle/caches/transforms-3/2cfe106a332c6a6f2245988cf5920bcb/transformed/jetified-kotlin-stdlib-common-1.6.10.jar!/META-INF/kotlin-stdlib-common.kotlin_module: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.6.0, expected version is 1.1.15.
	•	/Users/me/.gradle/caches/transforms-3/66d59e3c479db3ee1833aa8684ea1547/transformed/jetified-react-native-0.70.1-debug-api.jar!/META-INF/ReactAndroid_debug.kotlin_module: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.6.0, expected version is 1.1.15.
	•	/Users/me/.gradle/caches/transforms-3/8fa201288e2c6500b9e1af2f8ed9c77c/transformed/jetified-kotlin-stdlib-jdk7-1.6.10.jar!/META-INF/kotlin-stdlib-jdk7.kotlin_module: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.6.0, expected version is 1.1.15.
	•	/Users/me/.gradle/caches/transforms-3/b6d16718b0badf943a01bd505b26edc1/transformed/jetified-kotlin-stdlib-jdk8-1.6.10.jar!/META-INF/kotlin-stdlib-jdk8.kotlin_module: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.6.0, expected version is 1.1.15.
	•	/Users/me/.gradle/caches/transforms-3/d3fc290c9b603f2d0c573f31aa64957d/transformed/jetified-kotlin-stdlib-1.6.10.jar!/META-INF/kotlin-stdlib.kotlin_module: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.6.0, expected version is 1.1.15.
	•	PusherWebsocketReactNativeModule.kt
	•	PusherWebsocketReactNativePackage.kt

The error on Android Studio explains more with the Kotlin version. I however noticed kotlin_version is specifed in the pusher library and did not think it a good idea to temper with this, neither did i think it a good idea to explicitly specify kotlin_version in any section of the android

Undefined symbol _RCTEventEmitter

React Native version: 0.71.2
@pusher/pusher-websocket-react-native version: 1.2.1

I am currently seeing this error when building from XCode:
Screenshot 2023-02-25 at 9 12 39 PM

Pusher Authorization expecting wrong return

Hey guys, I'm having problems while using presence-channels, while on pusher-js the problems weren't happening, so, when I try subscribing to a presence-channel through my application the library seems to be expecting the wrong return from the backend side, according to this doc: https://pusher.com/docs/channels/library_auth_reference/auth-signatures/#worked-examples the auth route should return pusherKey: a hex digest of <socket_id>:<channel_name>:, and that's what we're doing on server side by using this method of pusher-http-go:
image
Where params is our socket_id and channel_name and memberData is our user data.
Here is the return of our authRoute, where the auth item is exactly what the doc tells and channel-data is the user info we used:

auth: ourPusherKey:76373596f2bba84a581abbc791d5461ac44ae979bceecf80c357e55372076412 channel_data: {"user_id":"9321318"}

but the following error occurs, telling that pusher isn't expecting a hex digest of <socket_id>:<channel_name>:<user_data> as it should be, but it is expecting a hex digest of <socket_id>:<channel_name> without the user_data.

Invalid signature: Expected HMAC SHA256 hex digest of 16085.17759627:presence-chat-app-D3C2DC7E-9553-46AC-B80E-6BD56C550E2F, but got 76373596f2bba84a581abbc791d5461ac44ae979bceecf80c357e55372076412

This is our pusher init config:
image

Opening this issue since everything seems to be following what is recommended by the documentation, and the auth route worked with pusher-js. Also the error is not clear, since it seems to me it should not expect only socket_id:channel_name since it is a presence-channel.

(Did not include our key/route/headers to avoid security problems)

Cant pod install

React Native Version: 0.68.2
pusher-websocket-react-native version: 1.0.2.
Bare react-native project

image

First, I install with platform: ios 12

image

I think the reason is my platform is not higher minimum deployment target
so I change platform 12 => 13.0 like your example
but I cant pod install
image

I have tried many ways, but it cant.
Does anyone have the same issue? please give me your solution, thanks

Getting error after trying to publish a client event on a private encrypted channel

Steps to reproduce

  • Open emulator
  • Subscribe to a private encrypted channel
  • On event edit text field: type client-event-example
  • On data edit text field: type 'bla'
  • Click Trigger Event

You'll see a page like this:
Screenshot 2022-06-28 at 19 15 46

Expected Behavior
Client events are not supported on private encrypted channels, so we need to handle it properly also on Client SDKs.
For further information: https://pusher.com/docs/channels/using_channels/encrypted-channels/

Conflict with other react-native modules (other libs using RCTDeviceEventEmitter with same event names)

The event name(s) emitted by the native module to JS are too generic and they conflict with other libs that emit the same event names.

My specific problem (conflict) is with Agora RTC react-native which uses a single event named "onEvent".

The React Native code says:

Currently, all native events are fired via a global RCTDeviceEventEmitter.
This means event names must be globally unique, and it means that call sites
can theoretically listen to RCTDeviceEventEmitter (although discouraged).

But I guess it's quite appealing for lib authors to call the event name onEvent :-)

A simple solution would be to just prefix all the event names with pusher: and update the JS to delegate with this prefix behind the scenes.

I'll fork and update the code with this change, since I'm blocked in my project by this issue.
And I'll get back with a PR, in case it's worth merging back in the master here.

Support for newer authenticate vs authorize seems to be missing

The newer JS libraries support a "signin" method as documented by pusher to be the preferred way of handing authentication, then authorization is handled differently as well. This library seems to only support the old single-srream authentication flow. Is this accurate? If so, why are we being told to use this react native library over the more mature JS library? I'm quite confused. Any guidance would be appreciated.

`pod install` fails

On a bare react native (not expo) project. Happens on v1.0.1 and v1.0.2

[!] CocoaPods could not find compatible versions for pod "pusher-websocket-react-native":
In Podfile:
pusher-websocket-react-native (from ../node_modules/@pusher/pusher-websocket-react-native)

Specs satisfying the pusher-websocket-react-native (from ../node_modules/@pusher/pusher-websocket-react-native) dependency were found, but they required a higher minimum deployment target.

React Native Web support

The package works well on Android and iOS platforms.
However, it doesn't seem to work on Web... Do you have a solution for working with pusher on React Native Web project ?

Custom authentication header on authEndpoint

Problem

Planning to add custom authentication header on authEndpoint at old server.

Previously in pusher-js/react-native, it is possible to add custom header through initialization, or subcribing:

new Pusher (<pusher-key>, {
  authEndpoint: <auth-endpoint>,
  auth: {
    headers: {
      Authorization: <your-token-key>,
      // Other headers
    }
  },
})

Through the documentation and brief code reading, it seems that it is not possible to achieve this, but i may be wrong, please educate me.

Expectation

Support on custom headers on authEndpoint.

403 Forbidden

ISSUE

/broadcasting/auth        403 Forbidden   

I am using laravel as the backend for this package, i cant seem to find a way to set the bearer token at the header in other to subscribe to to a private channel.

Snippet

const connect = async () => {
			try {
				await pusher.init({
					apiKey: "****",
					authEndpoint: 'https://....ngrok.io/broadcasting/auth',
					cluster: "mt1",
					onEvent: (event) => {
						console.log(event);
					}, 
					onSubscriptionSucceeded,
					onDecryptionFailure: (event) => {
						console.log('onDecryptionFailure', event);
					}
				});
				await pusher.connect();

				let myChannel = await pusher.subscribe({
					channelName: "my-channel", onEvent: (event) => {
						console.log(`onEvent: ${event}`);
					}
				});

				let privateChannel = await pusher.subscribe({
					channelName: `private-User.${user?.id}`, onEvent: (event) => {
						console.log(`onEvent: ${event}`);
					}
				});
				console.log(privateChannel);


			} catch (e) {
				console.log('ERROR: ' + e);
			}
		};

Support for user channels

No doubt on the roadmap but want to mention we've started our migration from pusher-js's RN module and have had to switch back from user channels to generic private channels. Would be great to have support for them in the new RN lib. Thanks.

Getting an error

Hello, I am getting this error.

D:\app\app\orderingapp2\android\app\build\generated\rncli\src\main\java\com\facebook\react\PackageList.java:15: error: package com.pusherwebsocketreactnative does not exist
import com.pusherwebsocketreactnative.PusherWebsocketReactNativePackage;
^
D:\app\app\orderingapp2\android\app\build\generated\rncli\src\main\java\com\facebook\react\PackageList.java:121: error: cannot find symbol
new PusherWebsocketReactNativePackage(),
symbol: class PusherWebsocketReactNativePackage

I am using
"@pusher/pusher-websocket-react-native": "^1.1.1"
"react-native": "^0.64.2"
OS: Windows 10
Platform: Android
Can you please let me know how I can solve it?

App Crashing when logging event on IOS

React Native Version: 0.69.2
pusher-websocket-react-native version: 1.0.2.
Bare react-native project

When logging event.data to the console the app crashes. If i log pusherEvent It logs the message successfully, but trying to access pusherEvent.data crashes the app. The app also crashes if you refresh the and receive an event. On the first load of the app everything works.

await pusher.subscribe( { channelName: "my-channel", onEvent: (event) => { console.log(Event received); console.log(event.data); } } );

error:
terminating with uncaught exception of type NSException *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Error when sending event: onEvent with body: { channelName = "my-channel"; data = "{\"message\":{\"id\":111,\"chat_room_id\":3,\"user_id\":1,\"parent_id\":null,\"has_attached_media\":null,\"job_attachment_id\":null,\"created_at\":\"2022-08-28T17:16:21.000000Z\",\"updated_at\":\"2022-08-28T17:16:21.000000Z\",\"message\":\"R\",\"user\":{\"id\":1,\"name\":\"Administrator\",\"email\":\"[email protected]\",\"email_verified_at\":null,\"created_at\":\"2022-08-07T03:14:11.000000Z\",\"updated_at\":\"2022-08-26T20:26:48.000000Z\",\"needs_approval\":0,\"needs_approval_by\":null,\"notify_poe_image\":0,\"avatar\":\"https:\\/\\/www.gravatar.com\\/avatar\\/09a7ea0fc3c71cd27275ccf23ddf2670?d=wavatar\"},\"room\":{\"id\":3,\"job_id\":2,\"room_title\":\"Test1\",\"user_id\":1,\"created_at\":\"1 day ago\",\"updated_at\":\"2022-08-26T21:25:45.000000Z\"}}}"; eventName = "App\\Events\\MyEvent"; userId = "<null>"; }. RCTCallableJSModules is not set. This is probably because you've explicitly synthesized the RCTCallableJSModules in PusherWebsocketReactNative, even though it's inherited from RCTEventEmitter.' CoreSimulator 802.6.1 - Device: iPhone 13 Pro (86ABA976-9834-4648-A06C-A3427A4D2DD6) - Runtime: iOS 15.5 (19F70) - DeviceType: iPhone 13 Pro

Problem with running on simulator and device arm64 vs x86

Hello, I am trying to add Pusher ( v 1.0.2) to my existing react native project (react native version 0.64.1). Though I have trouble doing so. First I get an error when trying to run it on my simulators node_modules/@pusher/pusher-websocket-react-native/ios/PusherWebsocketReactNative.swift:1:8: error: could not find module 'PusherSwift' for target 'arm64-apple-ios-simulator'; found: x86_64-apple-ios-simulator, x86_64,. When I try to run it on the real device I get multiple errors Undefined symbols for architecture arm64: and then a whole list of methods that have this problem. I am using XCode Version 13.0 (13A233). Does anybody know what might cause this issue? I am not running this code on a device with an M1 processor.

Random Crash Fatal Exception: java.lang.UnsupportedOperationException: JsonObject

What is seen in app
During development itself app would start and immediately crash. This is not always the behavior, at times app would start and then later randomly crash again. On looking more closely with Android Studio's logcat we are able to find the same error as listed on Firebase Crashlytics. The most weird thing is, this crash is only noticed in the application from around 8.30am morning to 7.00pm in evening.

Our use case involves using Channels to pass events from a backend to mobile application. It'll be of great help if someone can point towards a solution or possible causes triggering such an issue.

Can we share code-base
Unfortunately not

Exception

Fatal Exception: java.lang.UnsupportedOperationException: JsonObject
       at com.google.gson.JsonElement.getAsString(JsonElement.java:179)
       at com.pusher.client.channel.PusherEvent.fromJson(PusherEvent.java:88)
       at com.pusher.client.connection.websocket.WebSocketConnection.lambda$onMessage$5$WebSocketConnection(WebSocketConnection.java:212)
       at com.pusher.client.connection.websocket.-$$Lambda$WebSocketConnection$LH620kMWP6e8WFdl-2ZQYqgldrg.run(:4)
       at com.pusher.client.util.Factory.lambda$queueOnEventThread$0(Factory.java:140)
       at com.pusher.client.util.-$$Lambda$Factory$zJWSbSBBJ9pXjI_LpAkJ-3_Kedw.run(:2)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
       at java.lang.Thread.run(Thread.java:920)

Link error on Expo managed project

After using the expo install @pusher/pusher-websocket-react-native command on an expo managed project, I get the following error:

The package 'pusher-websocket-react-native' doesn't seem to be linked. Make sure:

  • You have run 'pod install'
  • You rebuilt the app after installing the package
  • You are not using Expo managed workflow

Pod install isn't available in a managed project, is it a bug or I am missing something?

Expo version 45.0.0
pusher-websocket-react-native version 1.1.0

How to determine if Pusher instance is already connected/subscribed? Alternatively how to avoid crashing due to fast refresh?

My RN environment uses fast refresh, which will run the constructor function after I make any changes to the code. On first run there are no issues and I connect/subscribe/receive events without issue. But once I make any changes at all the app will go through the constructor again and try to initialize Pusher a second time and gets as far as completing the onAuthorizer function and returning the data.

After this point it crashes and gives me the error "Exception in native call from JS" with the last stack trace reading:
onAuthorizer
PusherWebsocketReactNativeModule.kt:169

I have not been able to get around this. I cannot keep a record of having connected in my state because it resets as part of the refresh process with the new code, though the Pusher instance is obviously not reset this way.

I have tried disconnecting before I connect, and unsubscribing before I subscribe to my private channel but that has made no difference.

If I could do something like if(Pusher.isConnected) then I could avoid this problem but that does not seem to be an option?

How can I get past this problem?

Pod install step got error

Hi, I want to use this lib but I think we have problem about ios minimum targets.

Pod install end export log;

Analyzing dependencies
[!] CocoaPods could not find compatible versions for pod "pusher-websocket-react-native":
  In Podfile:
    pusher-websocket-react-native (from `../node_modules/@pusher/pusher-websocket-react-native`)

Specs satisfying the `pusher-websocket-react-native (from `../node_modules/@pusher/pusher-websocket-react-native`)` dependency were found, but they required a higher minimum deployment target.
error Command failed with exit code 1.

    "react": "17.0.2",
    "react-native": "0.66.0",

I changed minimum targets but didnt work.
Thank you.

onAuthorizer runs sequentially on Android

Problem

See #34 – it's almost the same problem, but on Android the app is still responsive, the native thread is blocked, though.

As a result, only one subscribe authorizer can be fired at the same time, because the first call will lock the mutex (and the thread).
The next one has to wait until the first one completes and if it does not complete (unlock the mutex) then the authorization logic is completely blocked.

The possible fix is the same as on iOS – remove the logic that relies on mutex in favor of a more lightweight blocking mechanism (like this) and a timeout in case JS does never return the auth object for a given subscription.

Repro steps:

  1. Install example app from this pusher repo on Android, commit b65a2c5fd885a2be4b1e9d1cbf9e8f5183622ef5
  2. Simulate multiple subscriptions fired at the same time:

Add this in connect:

await pusher.connect();
await pusher.subscribe({ channelName });
// add the lines below to fire extra subscriptions
setTimeout(async () => {
  await pusher.subscribe({ channelName: channelName + '_1' });
}, 100);
setTimeout(async () => {
  await pusher.subscribe({ channelName: channelName + '_2' });
}, 100);

Add this in onAuthorizer:

const onAuthorizer = async (channelName: string, socketId: string) => {
// this could also be await fetch(url), but long timeout is better for repro
console.warn('start ' + channelName);
await new Promise((resolve) => setTimeout(resolve, 5000));
console.warn('end ' + channelName);
  1. Start the app, fill in Pusher text inputs (remember to use private- channel prefix to trigger auth!)
  2. Tap Connect button and check the logs (console.warns)

Expectation: We should first see 3x start log and then 3x end log
Actual: It runs sequentially – first one start, then end+start and so it goes

FWIW – it works as expected on iOS here after removing the mutexes

onAuthorizer hangs iOS app

Problem

onAuthorizer callback must be used if one wants to implement a more complex auth logic (e.g. pass auth token as a request's header).
It is an asynchronous callback, meaning it can take time, depending on the user's network/server's load etc.
During this time the app should be responsive, so the user is able to interact with it and e.g. change the screen in the app.

However, currently onAuthorizer hangs the app until the necessary object is returned back through React Native bridge.
As a result, the app becomes unresponsive during authorization.

Repro steps:

  1. Install example app from this pusher repo, commit b65a2c5fd885a2be4b1e9d1cbf9e8f5183622ef5
  2. Simulate long async operation in the code:
// modify onAuthorizer implementation to this:
const onAuthorizer = async (channelName: string, socketId: string) => {
    // this could also be any await fetch(url), but long timeout is better for repro
    await new Promise((resolve) => setTimeout(resolve, 10000));
  1. Add extra button to test tap interaction during onAuthorizer a bit easier
<Image style={styles.image} source={require('./pusher.png')} />
<Button title="Click me" /> {/* Add this line for testing tap interaction */}
  1. Start the app, fill in Pusher text inputs (remember to use private- channel prefix to trigger auth!), confirm that the app is interactive - the new button works, the text inputs are clickable
  2. Tap Connect button and try tapping the new button

Expectation: the button can be tapped
Actual: the button doesn't do anything during await or stays in "tapped" state if one taps it right before onAuthorizer triggers, proving the app hangs

Video of the reproduction

HangExample.mov

Some extra notes

I debugged this a bit and it looks like the culprit is the usage of mutex for onAuthorizer.

In the Pusher RN code this line:
authorizerMutex[key]!.wait()
blocks the main thread, preventing further interaction until JS thread calls onAuthorizer.

Similarly, if we change the code in JS:

return {
      auth: pusherKey + ':' + signature,
      channel_data: user,
      shared_secret: 'foobar',
};

to e.g. simple return; or return undefined;, then the app will hang as well and never unblock itself.
It seems a bit risky and easy to miss, especially since Typescript types specify onAuthorizer return type as any.

Generally, the usage of mutex in React Native modules is not that common – I wonder if it would be possible to change this logic to something less blocking and avoid further app hangs.

Invalid key in subscription auth data

Hello team, we are trying to implement a react native pusher client for our existing infrastructure.

We are using onAuthorizer to manage our authentication flow, custom params...

  • here we return our secret:signature from our custom authentication flow
  • we are then returning the following object to our implementation:
    { auth: authResult.auth, channel_data: JSON.stringify({userId}), }
  • Subscription to private channel fails

yet we face an invalid key error referring to our secret chunk of the string as if it was trying to parse it as hash due to the : in the secret + : + signature.

I am completely puzzled about this... please advice.

`Invalid key in subscription auth data: 'SECRET_HERE'

  reactConsoleErrorHandler @ ExceptionsManager.js:149
  registerError @ LogBox.js:204
  console.error @ LogBox.js:67
  overrideMethod @ backend.js:2171
  onError @ NotificationService.ts:25
  (anonymous) @ index.tsx:158
  emit @ _EventEmitter.js:149
  __callFunction @ MessageQueue.js:419
  (anonymous) @ MessageQueue.js:116
  __guard @ MessageQueue.js:370
  callFunctionReturnFlushedQueue @ MessageQueue.js:115
  (anonymous) @ debuggerWorker.js:69
`

Cannot Dynamically change `authEndpoint` after init

Problem:
We have users who have multiple identities in our systems - when they logout and sign in under a new identity, they are unable to authenticate against private channels because the necessary URLs we use for authing users are dynamic to the user. When calling Pusher.getInstance() it returns the previously invoked instance stopping us from disconnecting & destroying the previous pusher instance and invoking a new one to init with the new authEndpoint

on iOS, it appears to work by just setting:

const pusher = Pusher.getInstance();
pusher.authEndpoint = '' // new endpoint

but when doing that on android, it crashes the app with IllegalStateException and message Cannot subscribe to a private or presence channel because no ChannelAuthorizer has been set. Call PusherOptions.setChannelAuthorizer() before connecting to Pusher.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.