FoodTech mobile apps: What is important to know about?
13 minutes
Business
Mobile
Apr 26, 2024
14 minutes
import CoreBluetooth
import CoreLocation
import React
import UserNotifications
// Declare the BeaconManager class, which implements interfaces for working with React Native and delegates for monitoring beacons and Bluetooth'
@objc(BeaconManager)
class BeaconManager: NSObject, RCTBridgeModule, CLLocationManagerDelegate, CBCentralManagerDelegate
{
// Module name for React Native
static func moduleName() -> String {
return "BeaconManager"
}
private var locationManager: CLLocationManager!
private var beaconRegion: CLBeaconRegion!
public var bridge: RCTBridge!
private var centralManager: CBCentralManager!
// Method for sending local notifications
func sendLocalNotification(with message: String) {
let content = UNMutableNotificationContent()
content.title = message // Notification title
content.body = "This is a region event" // Notification text
content.sound = .default // Notification sound
let request = UNNotificationRequest(
identifier: UUID().uuidString, content: content, trigger: nil) // Create a notification request
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil) // Adding a request to the notification center
}
// Start scanning beacons with the given UUID
@objc func startScanning(_ uuid: String, config: NSDictionary) {
// Request permission to send notifications
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) {
granted, error in
if granted {
print("Notifications allowed")
} else {
print("Notifications not allowed")
}
}
DispatchQueue.main.async {
self.locationManager = CLLocationManager() // Initialize CLLocationManager
self.locationManager.delegate = self // Setting the delegate
self.locationManager.requestAlwaysAuthorization() // Request for permanent access to geolocation
// Check and set settings for background scanning
self.locationManager.allowsBackgroundLocationUpdates = true
self.locationManager.pausesLocationUpdatesAutomatically = false
let uuid = UUID(uuidString: uuid)! // Convert UUID string to UUID
let beaconConstraint = CLBeaconIdentityConstraint(uuid: uuid) // Create a constraint for the beacon
self.beaconRegion = CLBeaconRegion(
beaconIdentityConstraint: beaconConstraint, identifier: "BeaconManagerRegion") // Initialize the beacon region
self.beaconRegion.notifyOnEntry = true // Notification when entering a region
self.beaconRegion.notifyOnExit = true // Notification when exiting a region
self.locationManager.startMonitoring(for: self.beaconRegion) // Start monitoring the region
self.locationManager.startRangingBeacons(in: self.beaconRegion) // Start determining the distance to beacons in the region
}
}
// Stop scanning beacons
@objc func stopScanning() {
if let beaconRegion = self.beaconRegion {
self.locationManager.stopMonitoring(for: beaconRegion) // Stop monitoring the region
self.locationManager.stopRangingBeacons(in: beaconRegion) // Stop determining the distance to beacons
self.beaconRegion = nil // Reset the beacon region
self.locationManager = nil // Reset CLLocationManager
}
}
// Initialize the Bluetooth manager
@objc func initializeBluetoothManager() {
centralManager = CBCentralManager(
delegate: self, queue: nil, options: [CBCentralManagerOptionShowPowerAlertKey: false])
}
// Handle Bluetooth state changes
func centralManagerDidUpdateState(_ central: CBCentralManager) {
var msg = ""
switch central.state {
case .unknown: msg = "unknown"
case .resetting: msg = "resetting"
case .unsupported: msg = "unsupported"
case .unauthorized: msg = "unauthorized"
case .poweredOff: msg = "poweredOff"
case .poweredOn: msg = "poweredOn"
@unknown default: msg = "unknown"
}
bridge.eventDispatcher().sendAppEvent(withName: "onBluetoothStateChanged", body: ["state": msg]) // Send Bluetooth state change event to React Native
}
// Request for permanent access to geolocation
@objc func requestAlwaysAuthorization(
_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock
) {
let locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
let status = CLLocationManager.authorizationStatus()
let statusString = statusToString(status)
resolve(["status": statusString])
}
// Request for access to geolocation when using the application
@objc func requestWhenInUseAuthorization(
_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock
) {
let locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
let status = CLLocationManager.authorizationStatus()
let statusString = statusToString(status)
resolve(["status": statusString])
}
// Get the current geolocation permission status
@objc func getAuthorizationStatus(
_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock
) {
let status = CLLocationManager.authorizationStatus()
resolve(statusToString(status))
}
// Handling region entry and region exit events
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
if let beaconRegion = region as? CLBeaconRegion {
sendLocalNotification(with: "Entered region: \(region.identifier)") // Send a notification about entering a region
if let bridge = self.bridge {
bridge.eventDispatcher().sendAppEvent(
withName: "onEnterRegion", body: ["region": beaconRegion.identifier]) // Dispatch a region entry event in React Native
}
}
}
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
if let beaconRegion = region as? CLBeaconRegion {
sendLocalNotification(with: "Exit region: \(region.identifier)") // Send a notification about leaving the region
if let bridge = self.bridge {
bridge.eventDispatcher().sendAppEvent(
withName: "onExitRegion", body: ["region": beaconRegion.identifier]) // Send a region exit event to React Native
}
}
}
// Handle detection of beacons in the region
func locationManager(
_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion
) {
let beaconArray = beacons.map { beacon -> [String: Any] in
return [
"uuid": beacon.uuid.uuidString, // UUID of the beacon
"major": beacon.major.intValue, // Major beacon value
"minor": beacon.minor.intValue, // Minor beacon value
"distance": beacon.accuracy, // Accuracy of the distance to the beacon
"rssi": beacon.rssi, // Beacon signal strength
]
}
if let bridge = bridge {
bridge.eventDispatcher().sendAppEvent(withName: "onBeaconsDetected", body: beaconArray) // Send data about detected beacons to React Native
}
}
// Handle geolocation permission changes
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
if #available(iOS 14.0, *) {
if manager.authorizationStatus == .authorizedAlways
|| manager.authorizationStatus == .authorizedWhenInUse
{
locationManager.startMonitoring(for: beaconRegion) // Start monitoring the region
locationManager.startRangingBeacons(in: beaconRegion) // Start determining the distance to beacons
}
} else {
if CLLocationManager.authorizationStatus() == .authorizedAlways
|| CLLocationManager.authorizationStatus() == .authorizedWhenInUse
{
locationManager.startMonitoring(for: beaconRegion)
locationManager.startRangingBeacons(in: beaconRegion)
}
}
}
//Helper method to convert geolocation permission status to a string
private func statusToString(_ status: CLAuthorizationStatus) -> String {
switch status {
case .notDetermined: return "notDetermined"
case .restricted: return "restricted"
case .denied: return "denied"
case .authorizedAlways: return "authorizedAlways"
case .authorizedWhenInUse: return "authorizedWhenInUse"
@unknown default: return "unknown"
}
}
}
#import "React/RCTBridgeModule.h"
@interface RCT_EXTERN_MODULE(BeaconManager, NSObject)
RCT_EXTERN_METHOD(startScanning:(NSString *)uuid config:(NSDictionary *)config)
RCT_EXTERN_METHOD(stopScanning)
RCT_EXTERN_METHOD(requestAlwaysAuthorization:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(requestWhenInUseAuthorization:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(getAuthorizationStatus:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(initializeBluetoothManager)
+ (BOOL)requiresMainQueueSetup {
return YES;
}
@end
import React, {useEffect, useState} from 'react';
import {View, NativeModules, Text} from 'react-native';
import {DeviceEventEmitter} from 'react-native';
const {BeaconManager} = NativeModules;
BeaconManager.requestAlwaysAuthorization();
BeaconManager.startScanning('FDA50693-A4E2-4FB1-AFCF-C6EB07647825');
const App = () => {
const [inRegion, setInRegion] = useState(false);
useEffect(() => {
DeviceEventEmitter.addListener('onBeaconsDetected', beacons => {
console.log('onBeaconsDetected', beacons);
});
}, []);
useEffect(() => {
DeviceEventEmitter.addListener('onEnterRegion', beacons => {
setInRegion(true);
});
}, []);
useEffect(() => {
DeviceEventEmitter.addListener('onExitRegion', beacons => {
setInRegion(false);
});
}, []);
return (
< View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 32,
backgroundColor: inRegion ? '#62BB46' : '#472F92',
}}>
<Text style={{color: '#fff', fontWeight: 600, fontSize: 16}}>
{inRegion
? 'You are within the range of the beacon'
: 'You are out of range of the beacon'}
</Text>
</View>
);
};
export default App;
If you are interested in this topic and want to figure out how you can use beacons in life, read our article "Interaction with customers offline via mobile app"