I have an IoT device which is sending iBeacon advertisements and want to detect these signals on my react-native application.
I found this library react-native-kontaktio. It works fine on android, but on ios it does not look like the "didDiscoverDevices" event gets triggered since the console.log is not showing up in my terminal. The app is running and i get no error message.
To configure this library for iOS i did the following:
yarn add react-native-kontaktio
cd ios
pod install
I have also included these permissions in info.plist:
NSBluetoothAlwaysUsageDescription, NSLocationAlwaysAndWhenInUseUsageDescription
This is my react-native version:
react-native-cli: 2.0.1,
react-native: 0.66.4.
This is the code which is working on android, but not on iOS.
import React, {useEffect, useState} from 'react';
import {
Alert,
DeviceEventEmitter,
NativeEventEmitter,
Platform,
PermissionsAndroid,
SafeAreaView,
Text,
StyleSheet,
Button,
} from 'react-native';
import Kontakt, {KontaktModule} from 'react-native-kontaktio';
const {connect, init, startDiscovery, startScanning} = Kontakt;
const kontaktEmitter = new NativeEventEmitter(KontaktModule);
const isAndroid = Platform.OS === 'android';
const isIOS = Platform.OS === 'ios';
const App = () => {
/**
* Android Marshmallow (6.0) and above need to ask the user to grant certain permissions.
* This function does just that.
*/
const requestLocationPermission = async () => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION,
{
title: 'Location Permission',
message:
'This example app needs to access your location in order to use bluetooth beacons.',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
return true;
} else {
// permission denied
return false;
}
} catch (err) {
console.warn(err);
return false;
}
};
const beaconSetup = async () => {
if (isAndroid) {
// Android
const granted = await requestLocationPermission();
if (granted) {
await connect();
await startScanning();
} else {
Alert.alert(
'Permission error',
'Location permission not granted. Cannot scan for beacons',
[{text: 'OK', onPress: () => console.log('OK Pressed')}],
{cancelable: false},
);
}
} else {
// iOS
console.log('IOS', isIOS);
await init();
await startDiscovery();
}
// Add beacon listener
if (isAndroid) {
DeviceEventEmitter.addListener(
'beaconsDidUpdate',
({beacons, region}) => {
console.log('beaconsDidUpdate', beacons, region);
console.log('REGION: ', region);
console.log('BEACONS', beacons);
}
);
} else {
console.log('IOS ADD LISTENER');
kontaktEmitter.addListener('didDiscoverDevices', ({beacons}) => {
console.log('didDiscoverDevices', beacons);
console.log('IOS DISCOVERED BEACON');
});
}
};
useEffect(() => {
beaconSetup();
return () => {
DeviceEventEmitter.removeAllListeners();
};
}, []);
I just solved the problem.
I called startRangingBeaconsInRegion(region) instead of startDiscovery() method and then it worked. I understood this after I read this sentence in documentation: "Discovery (i.e. didDiscoverDevices) can only detect Kontakt.io beacons. Ranging and monitoring also works with beacons of other manufacturers."
Related
I am using an older version of the capacitor geolocation, v1.3.1, and recently switched to the watchPosition implementation but occasionally that created a situation where the position is null or undefined even when the device is showing the location icon being active for the app. I tried to solve that by falling back to the slower getCurrentPosition function but still persists. Has anyone run into this issue before? Here is a gist of the hook.
https://gist.github.com/billpull/8bc6e49872cfee29aa5cef193b59c835
useCurrentPosition.ts
const useCurrentPosition = (): GeoWatchPositionResult => {
const [position, setPosition] = useState<Position>();
const [watchId, setWatchId] = useState("");
const [error, setError] = useState();
const clearWatch = () => {
if (watchId) {
clearPosition({ id: watchId });
setWatchId("");
}
};
const startWatch = async () => {
if (!watchId) {
const id = await watchPosition(async (pos: Position | null, err) => {
if (err) {
setError(err);
}
if (pos) {
setPosition(pos);
} else {
const newPosition = await getCurrentPosition();
setPosition(newPosition);
}
});
setWatchId(id);
}
};
useEffect(() => {
startWatch();
return () => clearWatch();
}, []);
return { currentPosition: position, error };
};
Even though the watchPosition is still returning location data on the interval I am getting a kCLErrorDomain error 1. which online says it means the permission was denied but thats not the case the phone was just in sleep mode. Is there a way to catch this error specifically? Should I clear the watch and restart it on this error?
Edit:
One attempt I made was to use a try catch in the watch, but I still have encountered this issue.
const useCurrentPosition = (): GeoWatchPositionResult => {
const [position, setPosition] = useState<Position>();
const [watchId, setWatchId] = useState("");
const [error, setError] = useState();
const clearWatch = () => {
if (watchId) {
clearPosition({ id: watchId });
setWatchId("");
}
};
const startWatch = async () => {
if (!watchId) {
const id = await watchPosition(async (pos: Position | null, err) => {
try {
if (err) {
setError(err);
}
if (pos) {
setPosition(pos);
} else {
const newPosition = await getCurrentPosition();
setPosition(newPosition);
}
} catch (ex) {
await requestPermission();
clearWatch();
await startWatch();
}
});
setWatchId(id);
}
};
useEffect(() => {
startWatch();
return () => clearWatch();
}, []);
return { currentPosition: position, error };
};
I think you should use this code snippet to check the current position.
import { Geolocation, Geoposition } from '#ionic-native/geolocation/ngx';
constructor(private geolocation: Geolocation) { }
...
let watch = this.geolocation.watchPosition();
watch.subscribe((data) => {
// data can be a set of coordinates, or an error (if an error occurred).
// data.coords.latitude
// data.coords.longitude
});
You can also use the following code snippet to get the current position.
this.geolocation.getCurrentPosition().then((resp) => {
// resp.coords.latitude
// resp.coords.longitude
}).catch((error) => {
console.log('Error getting location', error);
});
If you want to handle the permission denied error, the best way to handle the permission denied error is to check the permission status of the app first before trying to access any location data. This can be done by using the Capacitor Permissions API. If the permission has been granted, you can then proceed to use the watchPosition or getCurrentPosition APIs. If the permission has been denied, you can present a prompt to the user to request permission again.
setState is an asynchronous function, so please pass to it an anonymous function:
if (pos) {
setPosition(pos);
} else {
const newPosition = await getCurrentPosition();
setPosition(() => newPosition);
}
I use react-native-iap, And I have Non-Consumable so I use getAvailablePurchases() but in every time I get this error
Cannot connect to iTunes Store
So I can't test what I do while developing or if the user buying successfully or not or what the result of data should return when using getAvilablePurchases() or other methods
So is there a way to solve this issue?
Screen
Code
const items = Platform.select({
ios:"com.myapp.premium"
});
export default class PurchaseScreen extends Component {
componentDidMount() {
this.getPurchases();
}
getPurchases = async () => {
try {
const init = await RNIap.initConnection();
console.log('init?', init);
const availablePurchases = await RNIap.getAvailablePurchases(items);
console.log('availablePurchases', availablePurchases);
} catch (err) {
console.warn(err);
}
};
subscribe = ()=>{
...
}
render() {
return (
<TouchableOpacity onPress={()=>this.subscribe()} style={styles.btn}>
<Text style={styles.lable}>Subscrib</Text>
</TouchableOpacity>
)
}
}
You can't test in app purchases in the simulator.
However you will be able to get list of products ([SKProduct]).
I'm working on a JHipster application that I'm trying to get functioning in Electron. I have Golden Layout for window/pane management and cross-pane communication. I am having several problems with the combination of technologies, including:
I can't pop out more than one pane at the same time into their own Electron windows. I instead get an Uncaught Error: Can't create config, layout not yet initialised error in the console.
Two thirds of the panes don't display anything when popped out into Electron windows, and I'm not sure what the reason is. Any ideas or suggestions for this? One example of content is a leaflet map, another is a "PowerPoint preview" that is really just divs that mock the appearance of slides.
I haven't made it this far yet, but I assume that I will have trouble communicating between popped-out Electron windows when I get more than one open. Right now, the panes communicate between each other using Golden Layout's glEventHub emissions. I have an avenue to explore when I cross that bridge, namely Electron ipcRenderer.
Some borrowed code is here (most of it I can't share because it's company confidential):
electron.js:
const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const path = require('path');
const isDev = require('electron-is-dev');
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({width: 900, height: 680});
mainWindow.loadURL(isDev ? 'http://localhost:9000' : `file://${path.join(__dirname, '../build/index.html')}`);
if (isDev) {
// Open the DevTools.
//BrowserWindow.addDevToolsExtension('<location to your react chrome extension>');
mainWindow.webContents.openDevTools();
}
mainWindow.on('closed', () => mainWindow = null);
}
app.on('ready', createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (mainWindow === null) {
createWindow();
}
});
goldenLayoutComponent.tsx, a patch for Golden Layout:
import React from "react";
import ReactDOM from "react-dom";
// import "./goldenLayout-dependencies";
import GoldenLayout from "golden-layout";
import "golden-layout/src/css/goldenlayout-base.css";
import "golden-layout/src/css/goldenlayout-dark-theme.css";
import $ from "jquery";
interface IGoldenLayoutProps {
htmlAttrs: {},
config: any,
registerComponents: Function
}
interface IGoldenLayoutState {
renderPanels: Set<any>
}
interface IContainerRef {
current: any
}
export class GoldenLayoutComponent extends React.Component <IGoldenLayoutProps, IGoldenLayoutState> {
goldenLayoutInstance = undefined;
state = {
renderPanels: new Set<any>()
};
containerRef: IContainerRef = React.createRef();
render() {
const panels = Array.from(this.state.renderPanels || []);
return (
<div ref={this.containerRef as any} {...this.props.htmlAttrs}>
{panels.map((panel, index) => {
return ReactDOM.createPortal(
panel._getReactComponent(),
panel._container.getElement()[0]
);
})}
</div>
);
}
componentRender(reactComponentHandler) {
this.setState(state => {
const newRenderPanels = new Set(state.renderPanels);
newRenderPanels.add(reactComponentHandler);
return { renderPanels: newRenderPanels };
});
}
componentDestroy(reactComponentHandler) {
this.setState(state => {
const newRenderPanels = new Set(state.renderPanels);
newRenderPanels.delete(reactComponentHandler);
return { renderPanels: newRenderPanels };
});
}
componentDidMount() {
this.goldenLayoutInstance = new GoldenLayout(
this.props.config || {},
this.containerRef.current
);
if (this.props.registerComponents instanceof Function)
this.props.registerComponents(this.goldenLayoutInstance);
this.goldenLayoutInstance.reactContainer = this;
this.goldenLayoutInstance.init();
}
}
// Patching internal GoldenLayout.__lm.utils.ReactComponentHandler:
const ReactComponentHandler = GoldenLayout["__lm"].utils.ReactComponentHandler;
class ReactComponentHandlerPatched extends ReactComponentHandler {
_container: any;
_reactClass: any;
_render() {
const reactContainer = this._container.layoutManager.reactContainer; // Instance of GoldenLayoutComponent class
if (reactContainer && reactContainer.componentRender)
reactContainer.componentRender(this);
}
_destroy() {
// ReactDOM.unmountComponentAtNode( this._container.getElement()[ 0 ] );
this._container.off("open", this._render, this);
this._container.off("destroy", this._destroy, this);
}
_getReactComponent() {
// the following method is absolute copy of the original, provided to prevent depenency on window.React
const defaultProps = {
glEventHub: this._container.layoutManager.eventHub,
glContainer: this._container
};
const props = $.extend(defaultProps, this._container._config.props);
return React.createElement(this._reactClass, props);
}
}
GoldenLayout["__lm"].utils.ReactComponentHandler = ReactComponentHandlerPatched;
Any help or insight into these issues would be appreciated. Thanks in advance!
If you are still looking for a solutions, 1 and 2 I have solved, if you want to see my solution you could see in this repository.
But it was basically this:
1: The window that popups has a different path than the main window, so I just had to put a try catch in my requires, and you have to set
nativeWindowOpen = true
when creating the Browser window.
2: Solves it's self after 1 I think
i want to be able to scan QR code in my app with the front camera but all the available plugins supports only the main camera and dont have any option to capture from front cam, is there is any solution to this problem in the mainwhile?
You can configure it in the options of the scan function:
Future scan() async {
print("Scanning!!!");
try {
String barcode = await BarcodeScanner.scan(
options: ScanOptions(
useCamera: 1,
)
).then((value) { return value.rawContent;});
setState(() => this.barcode = barcode);
} catch (e) {
if (e.code == BarcodeScanner.cameraAccessDenied) {
setState(() {
this.barcode = 'The user did not grant the camera permission!';
});
} else {
setState(() => this.barcode = 'Unknown error: $e');
}
}
}
Using the number 1 in the useCamera property in ScanOptions means the front camera.
Hope it helps.
I do not know if this is possible but I might as well give it a chance and ask.
I'm doing an Electron app and I'd like to know if it is possible to have no more than a single instance at a time.
I have found this gist but I'm not sure hot to use it. Can someone shed some light of share a better idea ?
var preventMultipleInstances = function(window) {
var socket = (process.platform === 'win32') ? '\\\\.\\pipe\\myapp-sock' : path.join(os.tmpdir(), 'myapp.sock');
net.connect({path: socket}, function () {
var errorMessage = 'Another instance of ' + pjson.productName + ' is already running. Only one instance of the app can be open at a time.'
dialog.showMessageBox(window, {'type': 'error', message: errorMessage, buttons: ['OK']}, function() {
window.destroy()
})
}).on('error', function (err) {
if (process.platform !== 'win32') {
// try to unlink older socket if it exists, if it doesn't,
// ignore ENOENT errors
try {
fs.unlinkSync(socket);
} catch (e) {
if (e.code !== 'ENOENT') {
throw e;
}
}
}
net.createServer(function (connection) {}).listen(socket);;
});
}
There is a new API now: requestSingleInstanceLock
const { app } = require('electron')
let myWindow = null
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
app.quit()
} else {
app.on('second-instance', (event, commandLine, workingDirectory) => {
// Someone tried to run a second instance, we should focus our window.
if (myWindow) {
if (myWindow.isMinimized()) myWindow.restore()
myWindow.focus()
}
})
// Create myWindow, load the rest of the app, etc...
app.on('ready', () => {
})
}
Use the makeSingleInstance function in the app module, there's even an example in the docs.
In Case you need the code.
let mainWindow = null;
//to make singleton instance
const isSecondInstance = app.makeSingleInstance((commandLine, workingDirectory) => {
// Someone tried to run a second instance, we should focus our window.
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore()
mainWindow.focus()
}
})
if (isSecondInstance) {
app.quit()
}