Is it possible to broadcast with RSockets? - rsocket

My question:
I have implemented RSockets on my back-end (java spring boot) and on my front-end (React.js).
My front-end and back-end are able to communicate. However, I would like to know if it is possible to broadcast messages to multiple connected clients from my back-end.
Is this possible?

Yes it is possible.
Here is an example of a rsocket endpoint using spring webflux & kotlin coroutines which multicasts events to all it's observers.
This uses MutableSharedFlow, but this can be achieved with a PublishSubject or a Flowable in rxjava3.
In this example a rsocket server broadcasts a timestamp every 30 seconds to all the connected clients.
Controller:
#Controller
class ApiResource(
private val chatManager: ChatManager
) {
#MessageMapping("broadcast")
suspend fun broadcast(): Flow<Message> =
chatManager.broadcastStream()
}
Stream manager as well as a demo publisher:
#Service
class ChatManager {
val broadcast = MutableSharedFlow<Message>()
fun broadcastStream() = broadcast
#Scheduled(fixedRate = 30 * 1000)
fun sendBroadcast() = runBlocking{
broadcast.emit(
Message(
"SpringServer",
System.currentTimeMillis().toString(),
"broadcast"
)
)
}
}
React component BroadcastApp.tsx:
import React, {useEffect, useState} from 'react';
import {
BufferEncoders, encodeAndAddCustomMetadata, encodeAndAddWellKnownMetadata,
MESSAGE_RSOCKET_COMPOSITE_METADATA, MESSAGE_RSOCKET_ROUTING,
RSocketClient,
toBuffer, createBuffer
} from 'rsocket-core';
import RSocketWebSocketClient from 'rsocket-websocket-client';
const metadataMimeType = MESSAGE_RSOCKET_COMPOSITE_METADATA.string // message/x.rsocket.composite-metadata.v0
const dataMimeType = 'application/json';
const websocketEndpoint = 'ws://localhost:5051/rsocket';
const transport = new RSocketWebSocketClient({
url: websocketEndpoint,
}, BufferEncoders);
const endpoint = 'broadcast';
interface Message {
senderId: string;
text: string;
channelId: string;
}
type CONNECT_STATUS = 'disconnected' | 'connecting' | 'connected';
function BroadcastApp() {
const defaultMetadata = encodeAndAddWellKnownMetadata(
createBuffer(0),
MESSAGE_RSOCKET_ROUTING,
toBuffer(String.fromCharCode(endpoint.length) + endpoint)
)
const constructMetadataWithChannelId = (cid: string) => encodeAndAddCustomMetadata(
defaultMetadata,
'messaging/x.chat.client-id',
toBuffer(String.fromCharCode(cid.length) + cid)
)
const client = new RSocketClient({
setup: {
keepAlive: 60000,
lifetime: 180000,
dataMimeType,
metadataMimeType,
},
transport,
});
const [connectStatus, setConnectStatus] = useState<CONNECT_STATUS>('disconnected');
const [msgs, setMsgs] = useState<string[]>([]);
useEffect(() => {
if (connectStatus === 'connecting') {
console.log(`rsocket client connecting...`);
client
.connect()
.subscribe({
onError: error => {
console.log(`error: client connect: ${error}`)
setConnectStatus('disconnected');
},
onSubscribe: cancel => {},
onComplete: (sock) => {
sock.requestStream({
metadata: constructMetadataWithChannelId('broadcast')
}).subscribe({
onSubscribe: (subscription) => {
console.log(`rsocket client connected ✅`);
setConnectStatus('connected');
subscription.request(1000)
},
onNext: (event:any) => {
console.log(`received event from channel: ${JSON.stringify(event)}`);
const value = JSON.parse(event.data) as Message;
setMsgs(prev => [value.text, ...prev]);
},
onError: (error) => {
console.log(`err with rsocket subscribe: ${error}`)
}
});
}
});
}
}, [connectStatus])
const handleConnect = () => {
setConnectStatus('connecting');
};
const handleDisconnect = () => {
alert('todo: implement disconnect');
}
return (
<div style={{ padding: 20}}>
<div>
{(connectStatus === 'connected') ? (
<div style={{margin: 10, fontSize: 18}}>
<button
onClick={handleDisconnect}
style={{
padding: 10,
borderRadius: 10,
fontSize: 28,
margin: 10,
}}
>
Disconnect
</button>
</div>
) : (<div>
{connectStatus === 'disconnected' ? (<button
onClick={handleConnect}
style={{
padding: 10,
borderRadius: 10,
fontSize: 28,
}}
>
Connect
</button>) : (
<></>
)}
</div>)}
</div>
<div>
{msgs.map(item => (
<div
style={{
backgroundColor: 'lightgreen',
fontSize: 18,
width: 300,
padding: 10,
borderRadius: 10,
margin: 10,
}}>
{item}
</div>
))}
</div>
</div>
);
}
export default BroadcastApp;

Related

ReactJS: In App Purchase using in-app-purchase-2 not working

I have:
import { InAppPurchase2 as iap, IAPProduct } from "#ionic-native/in-app-purchase-2";
import { isPlatform } from '#ionic/react';
const IOS: React.FC = () => {
let history = useHistory();
const [productPrice, setPrice] = useState('')
const [productPriceA, setPriceA] = useState('')
const [product, setProduct] = useState([])
const [productA, setProductA] = useState([])
iap.validator = "https://validator.fovea.cc/v1/validate?###";
//initiate initInAppPurchase function
useEffect(() => {
const init = async () => {
await initInAppPurchase();
}
init();
}, []);
//if on an ios or android device, then get product info
const initInAppPurchase = () => {
if ((isPlatform('ios')) || (isPlatform('android'))) {
iap.verbosity = iap.DEBUG;
iap.register([
{
id: "tpmonthly",
alias: "Monthly",
type: iap.PAID_SUBSCRIPTION
}, {
id: "tpannual",
alias: "Annual",
type: iap.PAID_SUBSCRIPTION
},
]);
iap.ready(() => {
let product = iap.get('Monthly');
let productA = iap.get('Annual');
setPrice(product.price)
setProduct(product)
setPriceA(productA.price)
setProductA(productA)
})
iap.refresh();
}
}
//if user clicks purchase button
const purchaseProduct = () => {
if (product.owned) {
alert('A subscription is currently active.')
} else {
iap.order("tpmonthly").then(() => {
iap.when("tpmonthly").approved((p: IAPProduct) => {
p.verify();
p.finish();
});
history.push("/ios-signup/");
})
}
p.refresh();
}
//if user clicks purchase button
const purchaseProductA = () => {
if (product.owned) {
alert('A subscription is currently active.')
} else {
iap.order("tpannual").then(() => {
iap.when("tpannual").approved((p: IAPProduct) => {
p.verify();
p.finish();
});
history.push("/ios-signup/");
})
}
p.refresh();
}
return (
<>
<div className="wrapper" style={{ textAlign: 'center' }}>
<br/><br/>
<Button size="large" style={{ backgroundColor: '#202020', color: '#fff', width: '100%' }} variant="contained" onClick={purchaseProduct}>Subscribe Monthly for {productPrice}</Button><br/><br/>
<Button size="large" style={{ backgroundColor: '#202020', color: '#fff', width: '100%' }} variant="contained" onClick={purchaseProductA}>Subscribe Annually for {productPriceA}</Button><br/><br/>
</div>
</>
);
};
It displays the price of each subscription in the Button and when clicking asks me for my Apple login - but after completing this I get an error in Xcode with :
[store.js] ERROR: ios -> ERROR 6777010: An unknown error occurred - {"productId":"tpmonthly"}
Followed by:
[store.js] state: tpmonthly -> valid
So the subscription does not go through and does not show up in fovea.
Am pulling my hair out - is there anything I can do?

Local Push Notification not working on iOS, using React Native

I'm having trouble getting my local iOS push notification to work, using React Native. I've checked similar threads but haven't been able to find the answer. This is (meant to be) a basic setup, so hopefully someone can help.
I'm trying to fire the push notification on press of TouchableOpacity objects, wrapped within a FlatList. I'm not getting any code errors, the notification just isn't firing.
I've installed both npm packages as per the documentation, and I'm using React Native v0.65.1: -
npm install --save react-native-push-notification
npm i #react-native-community/push-notification-ios --save
I've added my code below. There are other files associated to other functionality on the app, but I haven't included them here, as this is the only file which includes code for push notification support.
Where am I going wrong?
Thanks in advance...
import {Alert, FlatList, Pressable, StyleSheet, Text, TextInput, TouchableOpacity, View} from "react-native";
import React, { useEffect, useState } from "react";
import GlobalStyle from '../utils/GlobalStyle';
import CustomButton from "../utils/CustomButton";
import SQLite from "react-native-sqlite-storage";
import { useDispatch, useSelector } from "react-redux";
import { setAge, setName, increaseAge, getCities } from "../redux/actions";
import PushNotification from "react-native-push-notification";
import PushNotificationIOS from "#react-native-community/push-notification-ios";
// store sqlite open database function
const db = SQLite.openDatabase(
{ name: 'sqlite_db', location: 'default' },
() => {},
error => { console.log(error) }
);
export default function Home({ navigation, route }) {
// replace local state objects with global redux objects
const { name, age, cities } = useSelector(state => state.userReducer);
const dispatch = useDispatch();
// function calls for SQLite and api data fetch
useEffect(() => {
getData();
dispatch(getCities());
}, []);
// fetch data objects from SQLite store
const getData = () => {
try {
db.transaction((tx) => {
tx.executeSql(
'SELECT Name, Age FROM Users',
[],
(tx, results) => {
let length = results.rows.length;
if (length > 0) {
let userName = results.rows.item(0).Name;
let userAge = results.rows.item(0).Age;
dispatch(setName(userName));
dispatch(setAge(userAge));
}
}
)
})
} catch (error) {
console.log(error);
}
}
// function to update SQLite data store
const updateData = async () => {
if (name.length == 0) {
Alert.alert('WARNING!', 'Please enter your name')
} else {
try {
db.transaction((tx) => {
tx.executeSql(
'UPDATE Users SET Name=?',
[name],
() => {
Alert.alert('SUCCESS!', 'Your data has been updated')
},
error => { console.log(error) }
)
})
} catch (error) {
console.log(error)
}
}
}
// function to remove data from SQLite data store
const removeData = async () => {
try {
db.transaction((tx) => {
tx.executeSql(
"DELETE FROM Users",
[],
() => {
navigation.navigate('Login')
},
error => { console.log(error) }
)
})
} catch (error) {
console.log(error)
}
}
**object to store function, fired when touchable is clicked
bound to touchable opacity onPress**
const handleNotification = (item) => {
PushNotification.localNotification({
title: 'Push Notification',
message: 'You clicked on' + item.country
})
}
return(
<View style={styles.body}>
<Text style={[
GlobalStyle.header,
GlobalStyle.text
]}>
Welcome {name} !
</Text>
**flatlist used to display api fetch responses
push notification onPress function is bound to TouchableOpacity object**
<FlatList
data={cities}
renderItem={({item}) => (
<TouchableOpacity
onPress={() => { handleNotification(item) }}
>
<View style={styles.item}>
<Text style={styles.title}>{item.country}</Text>
<Text style={styles.subtitle}>{item.city}</Text>
</View>
</TouchableOpacity>
)}
keyExtractor={(item, index) => index.toString()}
/>
</View>
)
}
const styles = StyleSheet.create({
body: {
flex: 1,
// justifyContent: 'center',
alignItems: 'center',
},
// now obsolete, as we have replaced this with a global style
text: {
// fontWeight: 'bold',
// fontSize: 26,
margin: 10,
// fontFamily: 'TitilliumWeb-Bold'
},
navButton: {
borderRadius: 11,
paddingHorizontal: 20,
width: 180,
height: 50,
justifyContent: 'center',
alignItems: 'center'
},
openDrawerButton: {
borderRadius: 11,
paddingHorizontal: 20,
width: 180,
height: 50,
justifyContent: 'center',
alignItems: 'center',
marginTop: 10
},
thirdFont: {
fontSize: 18,
paddingTop: 20,
fontFamily: 'Roboto-Regular'
},
textInput: {
width: 250,
height: 50,
marginTop: 10,
borderRadius: 10,
backgroundColor: '#d9cbe7',
borderWidth: 2,
borderColor: '#d9c1c1',
textAlign: 'center',
fontSize: 20
},
item: {
backgroundColor: 'white',
borderWidth: 3,
borderColor: '#bfcab8',
borderRadius: 7,
margin: 7,
width: 350,
justifyContent: 'center',
alignItems: 'center',
paddingVertical: 10
},
title: {
fontSize: 20,
fontFamily: 'Ubuntu-Bold',
color: '#a03f3f'
},
subtitle: {
marginTop: 8,
fontFamily: 'Ubuntu-Regular',
fontSize: 16
}
})
import PushNotificationIOS from "#react-native-community/push-notification-ios";
export default class NotifService {
constructor(onRegister, onNotification) {
this.configure(onRegister, onNotification);
this.lastId = 0;
}
configure(onRegister, onNotification, gcm = "") {
PushNotificationIOS.configure({
// (optional) Called when Token is generated (iOS and Android)
onRegister: onRegister, //this._onRegister.bind(this),
// (required) Called when a remote or local notification is opened or received
onNotification: onNotification, //this._onNotification,
// ANDROID ONLY: GCM Sender ID (optional - not required for local notifications, but is need to receive remote push notifications)
senderID: gcm,
// IOS ONLY (optional): default: all - Permissions to register.
permissions: {
alert: true,
badge: true,
sound: true
},
// Should the initial notification be popped automatically
// default: true
popInitialNotification: true,
/**
* (optional) default: true
* - Specified if permissions (ios) and token (android and ios) will requested or not,
* - if not, you must call PushNotificationsHandler.requestPermissions() later
*/
requestPermissions: true,
});
}
localNotif(title, message, sound, color) {
this.lastId++;
PushNotificationIOS.localNotificationSchedule({
/* Android Only Properties */
id: ''+this.lastId, // (optional) Valid unique 32 bit integer specified as string. default: Autogenerated Unique ID
ticker: "My Notification Ticker", // (optional)
autoCancel: true, // (optional) default: true
largeIcon: "ic_launcher", // (optional) default: "ic_launcher"
smallIcon: "ic_notification", // (optional) default: "ic_notification" with fallback for "ic_launcher"
bigText: message, // (optional) default: "message" prop
color: color, // (optional) default: system default
vibrate: true, // (optional) default: true
vibration: 300, // vibration length in milliseconds, ignored if vibrate=false, default: 1000
tag: 'some_tag', // (optional) add tag to message
group: "group", // (optional) add group to message
ongoing: false, // (optional) set whether this is an "ongoing" notification
/* iOS only properties */
alertAction: 'view', // (optional) default: view
category: '', // (optional) default: null
userInfo: {}, // (optional) default: null (object containing additional notification data)
/* iOS and Android properties */
title: title, // (optional)
message: message, // (required)
playSound: sound, // (optional) default: true
soundName: 'default', // (optional) Sound to play when the notification is shown. Value of 'default' plays the default sound. It can be set to a custom sound such as 'android.resource://com.xyz/raw/my_sound'. It will look for the 'my_sound' audio file in 'res/raw' directory and play it. default: 'default' (default sound is played)
actions: '["Yes", "No"]', // (Android only) See the doc for notification actions to know more
});
}
checkPermission(cbk) {
return PushNotification.checkPermissions(cbk);
}
cancelNotif() {
console.log(this.lastId, "***")
PushNotification.cancelLocalNotifications({id: ''+this.lastId});
}
cancelAll() {
PushNotification.cancelAllLocalNotifications();
}
}

ReactNative ble manager is not reading data from peripheral on IOS

I am building a ReactNative application for both IOS and Android platforms. My app needs to read data from another device via BLE communication. I am using this package for implementing BLE communication, https://github.com/innoveit/react-native-ble-manager. I am having a problem with receiving characteristic data on IOS even though it is working as expected on the Android platform.
I have added the following to the info.plist file:
Privacy - Bluetooth Always Usage Description: App needs to use Bluetooth to receive data from WaterRower machine
I have a component that scan and list down the BLE devices as follow. It is called, BleDeviceList.js
import React, {
useState,
useEffect,
} from 'react';
import {
SafeAreaView,
StyleSheet,
ScrollView,
View,
Text,
StatusBar,
NativeModules,
NativeEventEmitter,
Button,
Platform,
PermissionsAndroid,
FlatList,
TouchableHighlight,
} from 'react-native';
import {
Colors,
} from 'react-native/Libraries/NewAppScreen';
import BleManager from 'react-native-ble-manager';
const BleManagerModule = NativeModules.BleManager;
const bleManagerEmitter = new NativeEventEmitter(BleManagerModule);
const BleDeviceList = (props) => {
const [isScanning, setIsScanning] = useState(false);
const peripherals = new Map();
const [list, setList] = useState([]);
const [ connectedDevices, setConnectedDevices ] = useState([ ]);
const [ permissionsAllowed, setPermissionsAllowed ] = useState(false)
const startScan = () => {
if (!isScanning) {
BleManager.scan([], 3, true).then((results) => {
console.log('Scanning...');
setIsScanning(true);
}).catch(err => {
console.error(err);
});
}
}
const handleStopScan = () => {
console.log('Scan is stopped');
setIsScanning(false);
}
const handleDisconnectedPeripheral = (data) => {
let peripheral = peripherals.get(data.peripheral);
if (peripheral) {
peripheral.connected = false;
peripherals.set(peripheral.id, peripheral);
setList(Array.from(peripherals.values()));
}
console.log('Disconnected from ' + data.peripheral);
}
const handleUpdateValueForCharacteristic = (data) => {
console.log('Received data from ' + data.peripheral + ' characteristic ' + data.characteristic, data.value);
}
const retrieveConnected = () => {
BleManager.getConnectedPeripherals([]).then((results) => {
if (results.length == 0) {
console.log('No connected peripherals')
}
console.log(results);
for (var i = 0; i < results.length; i++) {
var peripheral = results[i];
peripheral.connected = true;
peripherals.set(peripheral.id, peripheral);
setList(Array.from(peripherals.values()));
}
});
}
const handleDiscoverPeripheral = (peripheral) => {
console.log('Got ble peripheral', peripheral);
if (!peripheral.name) {
peripheral.name = 'NO NAME';
}
peripherals.set(peripheral.id, peripheral);
setList(Array.from(peripherals.values()));
}
const isConnected = (peripheral) => {
return connectedDevices.filter(cd => cd.id == peripheral.id).length > 0;
}
const toggleConnectPeripheral = (peripheral) => {
if (peripheral){
if (isConnected(peripheral)){
BleManager.disconnect(peripheral.id);
setConnectedDevices(connectedDevices.filter(cd => cd.id != peripheral.id))
}else{
BleManager.connect(peripheral.id).then(() => {
let tempConnnectedDevices = [ ...connectedDevices ]
tempConnnectedDevices.push(peripheral);
setConnectedDevices(tempConnnectedDevices);
props.navigation.push('BleRowingSession', { peripheral: peripheral });
let p = peripherals.get(peripheral.id);
if (p) {
p.connected = true;
peripherals.set(peripheral.id, p);
setList(Array.from(peripherals.values()));
props.navigation.push('BleDeviceServiceList', { peripheral: peripheral });
}
console.log('Connected to ' + peripheral.id);
}).catch((error) => {
console.log('Connection error', error);
});
}
}
}
useEffect(() => {
BleManager.start({showAlert: false});
bleManagerEmitter.addListener('BleManagerDiscoverPeripheral', handleDiscoverPeripheral);
bleManagerEmitter.addListener('BleManagerStopScan', handleStopScan );
bleManagerEmitter.addListener('BleManagerDisconnectPeripheral', handleDisconnectedPeripheral );
bleManagerEmitter.addListener('BleManagerDidUpdateValueForCharacteristic', handleUpdateValueForCharacteristic );
if (Platform.OS === 'android' && Platform.Version >= 23) {
PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION).then((result) => {
if (result) {
console.log("Permission is OK");
setPermissionsAllowed(true);
} else {
PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION).then((result) => {
if (result) {
console.log("User accept");
setPermissionsAllowed(true);
} else {
console.log("User refuse");
setPermissionsAllowed(false);
}
});
}
});
} else {
setPermissionsAllowed(true)
}
return (() => {
console.log('unmount');
bleManagerEmitter.removeListener('BleManagerDiscoverPeripheral', handleDiscoverPeripheral);
bleManagerEmitter.removeListener('BleManagerStopScan', handleStopScan );
bleManagerEmitter.removeListener('BleManagerDisconnectPeripheral', handleDisconnectedPeripheral );
bleManagerEmitter.removeListener('BleManagerDidUpdateValueForCharacteristic', handleUpdateValueForCharacteristic );
})
}, []);
const renderConnectButton = (item) => {
if (isConnected(item)) {
return null
}
return (
<Button
title="Connect"
onPress={() => {
toggleConnectPeripheral(item)
}}
/>
)
}
const renderDisconnectButton = (item) => {
if (! isConnected(item)) {
return null
}
return (
<Button
title="Disconnect"
onPress={() => {
toggleConnectPeripheral(item)
}}
/>
)
}
const renderItem = (item) => {
const color = item.connected ? 'green' : '#fff';
return (
<TouchableHighlight>
<View style={[styles.row, {backgroundColor: color}]}>
<Text style={{fontSize: 12, textAlign: 'center', color: '#333333', padding: 10}}>{item.name}</Text>
<Text style={{fontSize: 10, textAlign: 'center', color: '#333333', padding: 2}}>RSSI: {item.rssi}</Text>
<Text style={{fontSize: 8, textAlign: 'center', color: '#333333', padding: 2, paddingBottom: 20}}>{item.id}</Text>
{renderConnectButton(item)}
{renderDisconnectButton(item)}
</View>
</TouchableHighlight>
);
}
const renderContent = () => {
if (! permissionsAllowed) {
return <Text>Bluetooth and locations permissions are required.</Text>
}
return (
<>
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={styles.scrollView}>
{global.HermesInternal == null ? null : (
<View style={styles.engine}>
<Text style={styles.footer}>Engine: Hermes</Text>
</View>
)}
<View style={styles.body}>
<View style={{margin: 10}}>
<Button
title={'Scan Bluetooth (' + (isScanning ? 'on' : 'off') + ')'}
onPress={() => startScan() }
/>
</View>
<View style={{margin: 10}}>
<Button title="Retrieve connected peripherals" onPress={() => retrieveConnected() } />
</View>
{(list.length == 0) &&
<View style={{flex:1, margin: 20}}>
<Text style={{textAlign: 'center'}}>No peripherals</Text>
</View>
}
</View>
</ScrollView>
<FlatList
data={list}
renderItem={({ item }) => renderItem(item) }
keyExtractor={item => item.id}
/>
</SafeAreaView>
</>
)
}
return (
renderContent()
);
};
const styles = StyleSheet.create({
scrollView: {
backgroundColor: Colors.lighter,
},
engine: {
position: 'absolute',
right: 0,
},
body: {
backgroundColor: Colors.white,
},
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
color: Colors.black,
},
sectionDescription: {
marginTop: 8,
fontSize: 18,
fontWeight: '400',
color: Colors.dark,
},
highlight: {
fontWeight: '700',
},
footer: {
color: Colors.dark,
fontSize: 12,
fontWeight: '600',
padding: 4,
paddingRight: 12,
textAlign: 'right',
},
});
export default BleDeviceList;
As you can see in the code, when the Connect button is clicked, it will redirect the user to another component that reads data from another device. The following is the BleRowingSession.js that reads the data from another device.
import React, { useEffect, useState } from 'react';
import { Text, View, StyleSheet, NativeModules, NativeEventEmitter, ScrollView } from 'react-native';
import BleManager from 'react-native-ble-manager';
const BleManagerModule = NativeModules.BleManager;
const bleManagerEmitter = new NativeEventEmitter(BleManagerModule);
const serviceId = "00001826-0000-1000-8000-00805F9B34FB";
const characteristicId = "00002AD1-0000-1000-8000-00805F9B34FB";
let readDataCache = "";
const BleRowingSession = (props) => {
let peripheral = props.route.params.peripheral;
const [ readData, setReadData ] = useState("");
const setUpBleNotification = () => {
BleManager.retrieveServices(peripheral.id).then((peripheralData) => {
console.log('Retrieved peripheral services', peripheralData);
setTimeout(() => {
BleManager.startNotification(peripheral.id, serviceId, characteristicId).then(() => {
console.log('Started notification on ' + peripheral.id);
bleManagerEmitter.addListener('BleManagerDidUpdateValueForCharacteristic', (data) => {
readDataCache = readDataCache + "\n" + data.value.toString()
setReadData(readDataCache);
});
setTimeout(() => {
}, 500);
}).catch((error) => {
console.log('Notification error', error);
});
}, 500)
});
}
useEffect(() => {
setUpBleNotification()
}, [ ])
return (
<View style={styles.container}>
<ScrollView>
<Text>Ble Rowing Session</Text>
<Text>{readData}</Text>
</ScrollView>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center"
}
})
export default BleRowingSession;
For now, the service id and characteristic id are hardcoded. Of course, I had to choose the right device on the first page that displays the list of BLE devices.
When I run the code, it is working as expected on the Android device, it is receiving the characteristic value. When I run the code on the actual IOS device, it is not receiving any data. But it can scan the devices and connect to them. What is wrong with my code and how can I fix it?

ipfs.cat returns "undefined" object in React-native

On React-native:
When I add a buffered string(i.e. Buffer(str) ) to IPFS using ipfs.add, the buffered string is added successfully and IPFS returns the hash.
When I try to retrieve the buffered string via ipfs.cat and the hash, ipfs.cat returns "undefined".
On Node.jS and ReactJs:
I do not have this problem. Both ipfs.add and ipfs.cat works.
Is the problem related to pinning in IPFS? or would changing the ipfs-api version in packag.json help?
Any help would be highly appreciated.
Below is the app.js code used for React-Native
import React, {useState, useRef} from 'react';
import {StyleSheet, Text, View, StatusBar, Button} from 'react-native';
const CryptoJS = require('crypto-js');
const ipfsAPI = require('ipfs-api');
// Connceting to the ipfs network via infura gateway
const ipfs = ipfsAPI('ipfs.infura.io', '5001', {protocol: 'https'});
export default function App() {
const [number, setNumber] = useState(0);
const [hash, setHash] = useState(' ');
console.log('printing: ', number);
//console.log('testing:', ipfs);
const handleCaseAdd = () => {
setNumber(1);
// Encrypt
const ciphertext = CryptoJS.AES.encrypt(
JSON.stringify('my message'),
'secret key 1234',
).toString();
console.log(' Ciphertext: ', ciphertext); // 'ciphertext
console.log('Buffered ciphertext: ', Buffer(ciphertext));
// Adding the encrpyted file to IPFS
ipfs.add(Buffer(ciphertext), {pin: true}, (error, result) => {
if (error) {
console.log(error);
return;
}
setHash(result[0].hash);
console.log('File added succesfully');
console.log('IPFS result: ', result);
});
}; // end of the function
const handleCaseGet = fileHash => {
//const fileHash = hash;
console.log('fileHash (before) :', fileHash);
ipfs.files.cat(fileHash, function(err, bufferedCiphertext) {
console.log('fileHash (after) :', fileHash);
console.log('Getting Buffered ciphertext: ', bufferedCiphertext);
});
}; // end of the function
//let confirmed;
//confirmed = true;
return (
<View style={styles.container}>
<View style={styles.first}>
<View style={styles.box1} />
<View style={styles.box2} />
</View>
<View>
<Button title="Add" onPress={handleCaseAdd} />
</View>
<Text> Number: {number} </Text>
<Text> Hash: {hash} </Text>
<View>
<Button title="Get" onPress={handleCaseGet.bind(this, hash)} />
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
//alignItems: "center",
//justifyContent: "center",
paddingTop: StatusBar.currentHeight,
},
first: {
backgroundColor: 'green',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'flex-start',
},
box1: {
backgroundColor: 'dodgerblue',
width: 50,
height: 50,
//flex: 1,
},
box2: {
backgroundColor: 'gold',
width: 50,
height: 50,
//alignSelf: "flex-start",
},
});
ipfs-api has been deprecated for two years, the replacement is ipfs-http-client - could you please try with that module instead?
https://www.npmjs.com/package/ipfs-http-client

React-native can't access Parse data

I'm trying to use Parse as the data provider for a ListView in a Reactive Native app. I have followed the Parse guide regarding subscribing to a query but for some unknown reason the the data source is empty. I have verified and writing a test object to Parse works fine.
It seems that observe() should be called before getInitialState() or am I missing something?
'use strict';
var React = require('react-native');
var Strings = require('./LocalizedStrings');
var Parse = require('parse').Parse;
var ParseReact = require('parse-react');
Parse.initialize("api_key_here", "api_key_here");
/*
var TestObject = Parse.Object.extend("TestObject");
var testObject = new TestObject();
testObject.save({foo: "bar"}).then(function(object) {
alert("yay! it worked");
});
*/
var {
View,
Text,
ListView,
StyleSheet
} = React;
var styles = StyleSheet.create({
mainContainer: {
flex: 1,
padding: 30,
marginTop: 65,
flexDirection: 'column',
justifyContent: 'center',
backgroundColor: '#fff'
},
title: {
marginBottom: 20,
fontSize: 22,
textAlign: 'center',
color: '#000'
},
});
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}) // assumes immutable objects
var WorkoutList = React.createClass({
mixins: [ParseReact.Mixin],
observe: function() {
return {
workouts: (new Parse.Query("Workout")).descending("createdAt")
};
},
getInitialState: function() {
return {dataSource: ds.cloneWithRows(this.data.workouts)}
},
renderRow: function() {
return (<View><Text>Testing</Text></View>)
},
render: function() {
return (
<View style = {{flex: 1, flexDirection: 'column'}}>
{Strings.workoutsTabTitle}
<ListView
ref = "listview"
dataSource = {this.state.dataSource}
renderRow = {this.renderRow}
automaticallyAdjustContentInsets = {false}
keyboardDismissMode = "onDrag"
keyboardShouldPersistTaps = {true}
showsVerticalScrollIndicator = {true}
style = {styles.mainContainer}
/>
</View>
)
}
})
module.exports = WorkoutList;
I didn't use ParseReact but the Parse Rest API to fetch data from Parse. The following code is called from componentDidMount.
fetch("https://api.parse.com/1/classes/Workout", {
headers: {
"X-Parse-Application-Id": "Your application Id",
"X-Parse-REST-API-Key": "Your API Key"
}
})
.then((response) => response.json())
.then((responseData) => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseData.results),
loaded: true,
})
})
.catch(function(error) {
console.log(error)
})
.done();
Using this approach you need to wait until the data is loaded before displaying the ListView. Use this.state.loaded to know when this is the case.
This works too.
observe: function() {
return {
user: ParseReact.currentUser,
abc: (new Parse.Query('abc')).descending('createdAt')
};
},
getInitialState: function () {
return {
dataSource: new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}),
};
},
render: function() {
return (
<View style={styles.full}>
<ListView
dataSource={this.state.dataSource.cloneWithRows(this.data.abc)}
renderRow={this.renderRow}
/>
</View>
);
},
Hope it helps! Cheers!

Resources