react native – I do not need bluetooth permission dialogue to look mechanically in iOS 13+

react native – I do not need bluetooth permission dialogue to look mechanically in iOS 13+


I’ve efficiently created an onboarding circulation that presents a bluetooth low power permission dialogue when the consumer faucets a proceed button in a specific display screen throughout onboarding. Nevertheless, it is solely working for Android. The app targets iOS 13 or newer. In iOS 13+, when the onboarding is launched the bluetooth permission dialogue seems mechanically. It does not look ahead to the consumer to faucet on the proceed button, within the second onboarding display screen, to activate it manually prefer it does in Android.

After launching the app for the primary time: I am requested if I wish to enable my app to seek out and hook up with gadgets on my native community, to which I agree. When the primary display screen of onboarding seems I am requested if I wish to enable bluetooth. I get the next log output:

LOG  Supervisor initialized, checking Bluetooth state...
LOG  Present Bluetooth state: Unknown
ERROR  Error enabling Bluetooth: [BleError: Bluetooth state change failed]

I faucet on okay to permit bluetooth, then faucet proceed to get to the bluetooth permission display screen. Once I faucet on proceed, the alert dialogue seems from the handleEnableBluetooth methodology in BluetoothScreen (see code snippet 2 under). I get the next output:

LOG  Requesting Bluetooth permission...
LOG  Bluetooth permission: unavailable
LOG  Location permission: unavailable
LOG  Location All the time permission: unavailable
ERROR  Bluetooth or Location permissions not granted
LOG  Bluetooth permission:  false

In my system settings bluetooth is enabled system large, and my app’s bluetooth toggle can be on.

My information.plist is:





    CFBundleDevelopmentRegion
    $(DEVELOPMENT_LANGUAGE)
    CFBundleDisplayName
    TychoCare
    CFBundleExecutable
    $(EXECUTABLE_NAME)
    CFBundleIdentifier
    $(PRODUCT_BUNDLE_IDENTIFIER)
    CFBundleInfoDictionaryVersion
    6.0
    CFBundleName
    $(PRODUCT_NAME)
    CFBundlePackageType
    $(PRODUCT_BUNDLE_PACKAGE_TYPE)
    CFBundleShortVersionString
    1.0.0
    CFBundleSignature
    ????
    CFBundleURLTypes
    
        
            CFBundleURLSchemes
            
                co.uk.tycho.provisioner
            
        
        
            CFBundleURLSchemes
            
                exp+provisioner
            
        
    
    CFBundleVersion
    14
    ITSAppUsesNonExemptEncryption
    
    LSMinimumSystemVersion
    13.3
    LSRequiresIPhoneOS
    
    NSAppTransportSecurity
    
        NSAllowsArbitraryLoads
        
        NSExceptionDomains
        
            localhost
            
                NSExceptionAllowsInsecureHTTPLoads
                
            
        
    
    NSBluetoothAlwaysUsageDescription
    $(PRODUCT_NAME) requires bluetooth entry to configure hubs and watches.
    NSBluetoothPeripheralUsageDescription
    $(PRODUCT_NAME) requires bluetooth entry to configure hubs and watches.
    NSLocationAlwaysUsageDescription
    $(PRODUCT_NAME) requires your location to seek out close by Bluetooth gadgets
    NSLocationWhenInUseUsageDescription
    $(PRODUCT_NAME) requires your location to seek out close by Bluetooth gadgets
    NSLocationAlwaysAndWhenInUseUsageDescription
    $(PRODUCT_NAME) requires your location
    NSCameraUsageDescription
    $(PRODUCT_NAME) wants entry to your digital camera to scan Tycho hub QR code to authorize you and take profile photos.
    NSMicrophoneUsageDescription
    $(PRODUCT_NAME) wants entry to your microphone.
    UIBackgroundModes
    
        bluetooth-peripheral
    
    UILaunchStoryboardName
    SplashScreen
    UIRequiredDeviceCapabilities
    
        armv7
    
    UIRequiresFullScreen
    
    UIStatusBarStyle
    
    UISupportedInterfaceOrientations
    
        UIInterfaceOrientationPortrait
        UIInterfaceOrientationPortraitUpsideDown
    
    UIUserInterfaceStyle
    Computerized
    UIViewControllerBasedStatusBarAppearance
    


My onboarding bluetooth display screen with the proceed button:

import React from "react";
import { View, Textual content, Alert } from "react-native";
import { useBluetoothConnection } from "../../Context/BLEContext";
import Icon from "react-native-vector-icons/Ionicons";
import AppButton from "../../Parts/ButtonComponent";
import Pagination from "../../Parts/PaginationComponent";
import ButtonStyle from "../../Kinds/ButtonStyle";
import Colours from "../../Kinds/ColorsStyle";
import Kinds from "../../Kinds/GeneralStyle";

const BluetoothScreen = ({ navigation }) => {
    const { requestPermissions } = useBluetoothConnection();

    const handleEnableBluetooth = async () => {
        console.log("Requesting Bluetooth permission...");
        const granted = await requestPermissions();
        console.log("Bluetooth permission: ", granted);
        if (granted) {
            navigation.navigate("Digital camera");
        } else {
            Alert.alert("Permission required", "Bluetooth permissions are required to proceed. Allow bluetooth in your system settings. Additionally, be certain location permissions are enabled for the app.");
        }
    };

    return (
        
            
                
                It's essential to allow bluetooth so you'll be able to configure Tycho hubs.
                Enabling bluetooth ensures you'll be able to hook up with Tycho hubs to configure them.
            
            
            
        
    );
};

export default BluetoothScreen;

My requestPermissions() function in my BLE context file:

import React, { createContext, useContext, useCallback, useState, useRef, useEffect } from "react";
import { Platform } from "react-native";
import { request, PERMISSIONS, RESULTS } from "react-native-permissions";
import { Buffer } from "buffer";

import { useActivityIndicator } from "./ActivityIndicatorContext.js";
import { useTheme } from "./ThemeContext.js";
import Colors from "../Styles/ColorsStyle.js";
import bleManagerSingleton from "../Singletons/BleManagerSingleton.js";
import {
    UART_SERVICE_UUID,
    UART_RX_CHARACTERISTIC_UUID,
    UART_TX_CHARACTERISTIC_UUID,
    EOT_MARKER
} from "../Constants/BLEConstants.js";

const BluetoothConnectionContext = createContext(null);

export const BluetoothConnectionProvider = ({ children }) => {
    const [scanHubComplete, setScanHubComplete] = useState(false);
    const [scanWatchComplete, setScanWatchComplete] = useState(false);
    const [scanBleDeviceComplete, setScanBleDeviceComplete] = useState(false);
    const [scanning, setScanning] = useState(false);
    const [connectedDevice, setConnectedDevice] = useState(null);

    const supervisor = bleManagerSingleton.getInstance();
    const operationQueue = useRef([]);
    const isOperationInProgress = useRef(false);

    const { showLoader, hideLoader } = useActivityIndicator();
    const { theme } = useTheme();

    const processQueue = useCallback(async (loaderMessage) => {
        if (operationQueue.present.size === 0 || isOperationInProgress.present) {
            return;
        }

        isOperationInProgress.present = true;
        const operation = operationQueue.present.shift();

        attempt  "Loading...");
            await operation();
         catch (error) {
            console.error("Error processing BLE operation:", error);
        } lastly {
            isOperationInProgress.present = false;
            hideLoader();
            processQueue(loaderMessage); // Course of the subsequent operation within the queue
        }
    }, [showLoader, hideLoader, theme.text]);

    const enqueueOperation = useCallback((operation, loaderMessage) => {
        operationQueue.present.push(operation);
        processQueue(loaderMessage);
    }, [processQueue]);

    const requestPermissions = useCallback(async () => {
        if (Platform.OS === "android") {
            attempt {
                if (Platform.Model >= 31) {
                    const scanGranted = await request(PERMISSIONS.ANDROID.BLUETOOTH_SCAN);
                    const connectGranted = await request(PERMISSIONS.ANDROID.BLUETOOTH_CONNECT);
                    const locationGranted = await request(PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION);

                    if (
                        scanGranted !== RESULTS.GRANTED ||
                        connectGranted !== RESULTS.GRANTED ||
                        locationGranted !== RESULTS.GRANTED
                    ) {
                        console.error("Bluetooth permissions not granted");
                        return false;
                    }
                } else {
                    const locationGranted = await request(PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION);

                    if (locationGranted !== RESULTS.GRANTED) {
                        console.error("Location permission not granted");
                        return false;
                    }
                }
                return true; // Permissions granted
            } catch (err) {
                console.warn(err);
                return false;
            }
        } else if (Platform.OS === "ios") {
            attempt {
                const bluetoothPermission = await request(PERMISSIONS.IOS.BLUETOOTH_PERIPHERAL);
                const locationPermission = await request(PERMISSIONS.IOS.LOCATION_WHEN_IN_USE);
                const locationAlwaysPermission = await request(PERMISSIONS.IOS.LOCATION_ALWAYS);

                console.log("Bluetooth permission:", bluetoothPermission);
                console.log("Location permission:", locationPermission);
                console.log("Location All the time permission:", locationAlwaysPermission);

                if (
                    bluetoothPermission !== RESULTS.GRANTED ||
                    locationPermission !== RESULTS.GRANTED ||
                    locationAlwaysPermission !== RESULTS.GRANTED
                ) {
                    console.error("Bluetooth or Location permissions not granted");
                    return false;
                }
                return true; // Permissions granted
            } catch (err) {
                console.warn(err);
                return false;
            }
        }
        // Default return true for platforms apart from Android and iOS
        return true;
    }, []);

    const checkBluetoothState = useCallback(async () => {
        return enqueueOperation(async () => {
            const state = await supervisor.state();
            console.log("Present Bluetooth state:", state);
            if (state !== "PoweredOn") {
                attempt {
                    await supervisor.allow();
                } catch (error) {
                    console.error("Error enabling Bluetooth:", error);
                }
            }
        }, "Checking Bluetooth state...");
    }, [manager, enqueueOperation]);

    useEffect(() => {
        if (supervisor) {
            console.log("Supervisor initialized, checking Bluetooth state...");
            checkBluetoothState();
        } else {
            console.error("BleManager just isn't out there");
        }
    }, [manager]);

    const startDeviceScan = useCallback((deviceType) => {
        return new Promise((resolve, reject) => {
            enqueueOperation(async () => {
                if (Platform.OS === "android" && Platform.Model >= 31) {
                    const permissionsGranted = await requestPermissions();
                    if (!permissionsGranted) {
                        reject(new Error("Permissions not granted"));
                        return;
                    }
                }
                console.log("Beginning system scan...");
                attempt {
                    setScanning(true);
                    let bestDevice = null;
                    supervisor.startDeviceScan(null, null, (error, scannedDevice) => {
                        if (error) {
                            console.error("Error throughout scan:", error);
                            if (deviceType === "Tycho-Hub") {
                                setScanHubComplete(true);
                            } else if (deviceType === "Tycho-Watch") {
                                setScanWatchComplete(true);
                            } else {
                                setScanBleDeviceComplete(true);
                            }
                            setScanning(false);
                            reject(error);
                            return;
                        }

                        if (scannedDevice.title && scannedDevice.title.consists of(deviceType)) {
                            // Verify if this system has the very best RSSI
                            if (!bestDevice || scannedDevice.rssi > bestDevice.rssi) {
                                bestDevice = scannedDevice;
                            }
                        }
                    });

                    // Cease scanning after a sure interval or situation
                    setTimeout(() => {
                        if (bestDevice) {
                            if (deviceType === "Tycho-Hub") {
                                setScanHubComplete(true);
                            } else if (deviceType === "Tycho-Watch") {
                                setScanWatchComplete(true);
                            } else {
                                setScanBleDeviceComplete(true);
                            }
                            console.log("Greatest system discovered:", bestDevice);
                            resolve(bestDevice);
                        } else {
                            reject(new Error("No system discovered"));
                        }
                        stopDeviceScan();
                        setScanning(false);
                    }, 7500); // Modify the timeout length as wanted

                } catch (e) {
                    console.error("Exception throughout startDeviceScan:", e);
                    reject(e);
                }
            }, "Scanning...");
        });
    }, [stopDeviceScan, enqueueOperation, requestPermissions]);

    const stopDeviceScan = useCallback(() => {
        return enqueueOperation(async () => {
            supervisor.stopDeviceScan();
        }, "Stopping system scan...");
    }, [enqueueOperation]);

    const connectToDevice = useCallback(async (system) => {
        return enqueueOperation(async () => {
            attempt {
                //await checkBluetoothState();  // Guarantee Bluetooth is powered on
                console.log("Gadget in connectToDevice:", system);
                const newlyConnectedDevice = await supervisor.connectToDevice(system.id);
                console.log(`Related to system: ${newlyConnectedDevice.localName}`);
                setConnectedDevice(newlyConnectedDevice);
            } catch (error) {
                console.error("Error connecting to system:", error);
                throw new Error(error);
            }
        }, "Connecting to system...");
    }, [enqueueOperation]);

    const disconnectFromDevice = useCallback(async (system) => {
        return enqueueOperation(async () => {
            attempt {
                await supervisor.cancelDeviceConnection(system.id);
            } catch (error)  "Unknown error", error.code);
            
        }, "Disconnecting from system...");
    }, [enqueueOperation]);

    const characteristicsForService = useCallback(async (system) => {
        if (!system) {
            throw new Error("Gadget just isn't related to the app");
        }
        const service = await system.discoverAllServicesAndCharacteristics();
        const traits = await service.characteristicsForService(UART_SERVICE_UUID);
        return traits;
    }, []);

    const obtainUartServiceAndCharacteristics = useCallback(async (system) => {
        const traits = await characteristicsForService(system);
        const rxCharacteristic = traits.discover(c => c.uuid === UART_RX_CHARACTERISTIC_UUID);
        const txCharacteristic = traits.discover(c => c.uuid === UART_TX_CHARACTERISTIC_UUID);
        if (!rxCharacteristic) {
            throw new Error("RX attribute not discovered");
        }
        if (!txCharacteristic) {
            throw new Error("TX attribute not discovered");
        }
        return { rxCharacteristic, txCharacteristic };
    }, [characteristicsForService]);

    const receiveFromHub = useCallback((system) => {
        console.log("Receiving from hub...");

        return new Promise((resolve, reject) => {
            enqueueOperation(() => {
                console.log("Gadget inside receiveFromHub is: ", system);
                console.log(`Gadget is ${system.isConnected() ? "related" : "not related"}`);
                if (system && !system.isConnected()) {
                    reconnect(system); // Name reconnect straight with out enqueuing
                }
                console.log("Gadget in receiveFromHub: ", system);

                let fullData = "";
                let timeoutHandle;

                attempt {
                    const subscription = supervisor.monitorCharacteristicForDevice(
                        system.id,
                        UART_SERVICE_UUID,
                        UART_TX_CHARACTERISTIC_UUID,
                        (error, attribute) => {
                            if (error) {
                                console.error("Notification error:", error);
                                cleanup();
                                reject(error);
                                return;
                            }

                            const knowledge = attribute.worth;
                            const decodedData = Buffer.from(knowledge, "base64").toString("ascii");
                            console.log("Acquired notification knowledge:", decodedData);

                            if (decodedData.consists of(EOT_MARKER)) {
                                console.log("Full knowledge obtained:", fullData);
                                cleanup();
                                resolve(fullData);
                            } else {
                                fullData += decodedData;
                            }
                        }
                    );

                    // Set a timeout to reject the promise if knowledge just isn't obtained in time
                    const TIMEOUT_DURATION = 5000; // 5 seconds
                    timeoutHandle = setTimeout(() => {
                        console.error("Timeout reached with out receiving full knowledge");
                        cleanup();
                        reject(new Error("Timeout reached"));
                    }, TIMEOUT_DURATION);

                    // Cleanup operate to take away subscription and clear timeout
                    const cleanup = () => {
                        if (subscription) {
                            subscription.take away();
                        }
                        clearTimeout(timeoutHandle);
                    };

                } catch (error) {
                    console.error("Error subscribing to notifications:", error);
                    reject(error);
                }
            }, "Receiving from hub...");
        });
    }, [manager, enqueueOperation, reconnect]);

    const sendToHub = useCallback(async (system, dataToSend) => {
        console.log("Sending to hub: ", dataToSend);
        return enqueueOperation(async () => {
            console.log("Gadget inside sendToHub is: ", system);
            console.log(`Gadget is ${system.isConnected() ? "related" : "not related"}`);
            if (system && !system.isConnected()) {
                reconnect(system);
            }
            console.log("Gadget in sendToHub: ", system);
            attempt {
                const { rxCharacteristic } = await obtainUartServiceAndCharacteristics(system);
                await rxCharacteristic.writeWithoutResponse(dataToSend);
                console.log("Information despatched to hub:", dataToSend);
            } catch (error) {
                console.error("Error in sendToHub:", error);
                throw new Error(error);
            }
        }, "Sending to hub...");
    }, [obtainUartServiceAndCharacteristics, enqueueOperation]);

    const requestConnectionPriority = useCallback(async (precedence) => {
        return enqueueOperation(async () => {
            attempt {
                const system = await supervisor.gadgets([device.id]);
                if (system && await system.isConnected()) {
                    await system.requestConnectionPriority(precedence);
                    console.log(`Requested connection precedence: ${precedence} for system: ${system.id}`);
                } else {
                    console.error("Gadget just isn't related");
                }
            } catch (error)  "Unknown error", error.code);
            
        }, "Requesting connection precedence...");
    }, [manager, enqueueOperation]);

    const reconnect = useCallback(async (system) => {
        attempt {
            await disconnectFromDevice(system);
            await connectToDevice(system);
        } catch (error) {
            console.error("Reconnection failed:", error);
            setTimeout(() => reconnect(system), 5000); // Retry after 5 seconds
        }
    }, [disconnectFromDevice, connectToDevice]);

    useEffect(() => {
        const subscription = supervisor.onDeviceDisconnected((error, system) => {
            if (error) {
                console.error("Gadget disconnected:", error);
                reconnect(system);
            }
        });
        return () => subscription.take away();
    }, [manager, reconnect]);

    return (
        
            {youngsters}
        
    );
};

export const useBluetoothConnection = () => {
    const context = useContext(BluetoothConnectionContext);
    if (!context) {
        throw new Error("useBluetoothConnection have to be used inside a BluetoothConnectionProvider");
    }
    return context;
};

Leave a Reply

Your email address will not be published. Required fields are marked *