London Escorts sunderland escorts 1v1.lol unblocked yohoho 76 https://www.symbaloo.com/mix/yohoho?lang=EN yohoho https://www.symbaloo.com/mix/agariounblockedpvp https://yohoho-io.app/ https://www.symbaloo.com/mix/agariounblockedschool1?lang=EN
7.8 C
New York
Thursday, November 14, 2024

swift – The way to Allow BLE Scanning within the Background on iOS


I’m making an attempt to implement background BLE (Bluetooth Low Power) scanning in an iOS app utilizing CoreBluetooth. When my app is within the foreground, I can efficiently scan and acquire BLE machine information. Nonetheless, when the app goes into the background or the telephone is locked, the scanning doesn’t operate as anticipated. The app doesn’t obtain BLE information within the background. I need to scan and ship the BLE to a server each 20 seconds when a location background replace is made. Right here is an summary of my setup:

The BluetoothManager:

import Basis
import CoreBluetooth

struct BluetoothDevice {
    var peripheral: CBPeripheral
    var rssi: NSNumber
    var advertisementData: [String: Any]
    var manufacturerData: Information?
    var main: UInt16?
    var minor: UInt16?
}

class BluetoothManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeripheralDelegate {
    @Revealed var discoveredDevices: [BluetoothDevice] = []
    @Revealed var connectedPeripherals: [CBPeripheral: [CBService]] = [:] // To retailer companies for every peripheral
    @Revealed var isScanning: Bool = false
    
    personal var centralManager: CBCentralManager!
    
    override init() {
        tremendous.init()
        centralManager = CBCentralManager(delegate: self, queue: nil)
    }
    
    func startScanning() {
        if !isScanning {
            isScanning = true
            discoveredDevices.removeAll()
            centralManager.scanForPeripherals(withServices: nil)
            
            // Cease scanning after 2 seconds
            DispatchQueue.major.asyncAfter(deadline: .now() + 2) {
                self.stopScanning()
            }
        }
    }
    
    func stopScanning() {
        if isScanning {
            centralManager.stopScan()
            isScanning = false
            print("Stopped scanning for gadgets")
        }
    }
    
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        if central.state == .poweredOn {
            startScanning()
        } else {
            print("Bluetooth will not be out there.")
        }
    }
    
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
        // Extract producer information if out there
        guard let manufacturerData = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Information else {
            return
        }
        
        // Assuming that is an iBeacon-like machine
        let manufacturerDataHex = manufacturerData.hexEncodedString().lowercased()
        
        // Filter: Examine if producer information accommodates "7e10ca7"
        if manufacturerDataHex.accommodates("exampleHexNumber") {
            var majorValue: UInt16? = nil
            var minorValue: UInt16? = nil
            
            // Examine if the information size is sufficient to comprise main and minor values
            if manufacturerData.depend >= 24 {
                // Extract main and minor values
                let majorRange = 20..<22
                let majorData = manufacturerData.subdata(in: majorRange)
                majorValue = UInt16(bigEndian: majorData.withUnsafeBytes { $0.load(as: UInt16.self) })
                
                let minorRange = 22..<24
                let minorData = manufacturerData.subdata(in: minorRange)
                minorValue = UInt16(bigEndian: minorData.withUnsafeBytes { $0.load(as: UInt16.self) })
                
                // Examine if this machine is already in discoveredDevices
                if let existingIndex = discoveredDevices.firstIndex(the place: { $0.peripheral.identifier == peripheral.identifier }) {
                    // Replace RSSI of the present machine
                    discoveredDevices[existingIndex].rssi = RSSI
                } else {
                    // Add new machine if it is not already in discoveredDevices
                    let machine = BluetoothDevice(
                        peripheral: peripheral,
                        rssi: RSSI,
                        advertisementData: advertisementData,
                        manufacturerData: manufacturerData,
                        main: majorValue,
                        minor: minorValue
                    )
                    discoveredDevices.append(machine)
                }
            }
        }
    }
    
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        //print("Linked to (peripheral.title ?? "Unknown Machine")")
        peripheral.discoverServices(nil)
    }
    
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        if let error = error {
            print("Error discovering companies: (error.localizedDescription)")
            return
        }
        
        if let companies = peripheral.companies {
            DispatchQueue.major.async {
                self.connectedPeripherals[peripheral] = companies // Retailer the found companies
                self.updateDeviceList() // Notify that the record must be up to date
            }
        }
    }
    
    func updateDeviceList() {
        objectWillChange.ship() // Notify the UI that the information has modified
    }
    
    // Helper operate to extract serial quantity (customized logic relying in your machine information)
    func extractSerialNumber(from manufacturerData: Information?, advertisementData: [String: Any]) -> String {
        // Instance logic: Attempt to extract from native title or parse particular a part of producer information
        if let localName = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
            return "SN-(localName)"
        } else if let manufacturerData = manufacturerData {
            // Instance: Assume the serial quantity is saved in bytes 3-8 of producer information (customise as per your machine)
            let serialNumberData = manufacturerData.subdata(in: 2..<8)
            return serialNumberData.hexEncodedString().uppercased()
        } else {
            return "Unknown"
        }
    }
}

extension Information {
    /// Convert the producer information to a hex string for simpler studying
    func hexEncodedString() -> String {
        return map { String(format: "%02hhx", $0) }.joined()
    }
}

And the Location Supervisor:

import CoreLocation
import Basis

class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
    personal var locationManager = CLLocationManager()
    personal var bluetoothManager = BluetoothManager()
    personal var lastSentTimestamp: Date?  // Observe the final time information was despatched
    
    @Revealed var locationStatus: CLAuthorizationStatus?
    @Revealed var lastLocation: CLLocation?
    @Revealed var lastSuccessfulRequestDate: Date?  // New printed property for final profitable request
    
    override init() {
        tremendous.init()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.requestAlwaysAuthorization()
        locationManager.showsBackgroundLocationIndicator = true
        locationManager.allowsBackgroundLocationUpdates = true
        locationManager.pausesLocationUpdatesAutomatically = false
        locationManager.startUpdatingLocation()
    }
    
    func locationManager(
        _ supervisor: CLLocationManager,
        didUpdateLocations areas: [CLLocation]
    ) {
        guard let location = areas.final else { return }
        lastLocation = location
        
        // Examine if we should always ship information primarily based on the 20-second interval
        if shouldSendLocationData() {
            bluetoothManager.startScanning()  // Begin Bluetooth scan
            
            // Delay sending location information to permit BLE scan to finish
            DispatchQueue.major.asyncAfter(deadline: .now() + 2) {
                self.sendLocationData(location)
            }
        }
    }
    
    func locationManager(
        _ supervisor: CLLocationManager,
        didChangeAuthorization standing: CLAuthorizationStatus
    ) {
        self.locationStatus = standing
    }
    
    personal func shouldSendLocationData() -> Bool {
        if let lastSent = lastSentTimestamp {
            return Date().timeIntervalSince(lastSent) >= 20
        }
        return true
    }
    
    personal func sendLocationData(_ location: CLLocation) {
        // Guarantee we're respecting the interval examine
        guard shouldSendLocationData() else { return }
        
        // Replace the lastSentTimestamp after passing the interval examine
        lastSentTimestamp = Date()
        
        guard let url = URL(string: "https://exampleurl.com/submit") else {
            print("Invalid URL")
            return
        }
        
        let timestamp = Int(location.timestamp.timeIntervalSince1970)
        
        let truncatedLatitude = location.coordinate.latitude
        let truncatedLongitude = location.coordinate.longitude

        // Gather BLE measurements with a single date for the BLE part
        let bleMeasurements = bluetoothManager.discoveredDevices.map { machine -> [String: Any] in
            var measurement: [String: Any] = ["rssi": device.rssi.intValue]
            if let minor = machine.minor {
                measurement["minor"] = minor
            }
            return measurement
        }
        
        // Create BLE information with one date and a number of measurements
        let bleData: [String: Any] = [
            "date": Int(Date().timeIntervalSince1970),
            "measurements": bleMeasurements
        ]
        
        // Create GNSS information
        let gnssData: [String: Any] = [
            "date": timestamp,
            "data": [
                "latitude": truncatedLatitude,
                "longitude": truncatedLongitude
            ]
        ]
        
        // Assemble the ultimate JSON dict with each GNSS and BLE information
        let jsonDict: [String: Any] = [
            "ble": [bleData],  // Single BLE entry with one date and a number of measurements
            "gnss": [gnssData]
        ]
        
        // Convert information to JSON
        guard let jsonData = attempt? JSONSerialization.information(withJSONObject: jsonDict, choices: .prettyPrinted) else {
            print("Didn't serialize JSON")
            return
        }
        
        // Print JSON as a debug output
        if let jsonString = String(information: jsonData, encoding: .utf8) {
            print("JSON Payload:n(jsonString)")
        }
        
        guard let username = KeychainHelper.load(key: "title"),
              let password = KeychainHelper.load(key: "password") else {
            print("Credentials not discovered")
            return
        }
        
        let loginString = "(username):(password)"
        guard let loginData = loginString.information(utilizing: .utf8) else {
            print("Didn't encode credentials")
            return
        }
        let base64LoginString = loginData.base64EncodedString()
        
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("utility/json", forHTTPHeaderField: "Content material-Sort")
        request
            .setValue(
                "Primary (base64LoginString)",
                forHTTPHeaderField: "Authorization"
            )
        request.httpBody = jsonData
        
        let job = URLSession.shared.dataTask(
            with: request
        ) {
            information,
            response,
            error in
            if let error = error {
                print(
                    "Error sending location information: (error.localizedDescription)"
                )
                return
            }
            
            if let httpResponse = response as? HTTPURLResponse,
               httpResponse.statusCode == 200 {
                // Replace final profitable request date on success
                DispatchQueue.major.async {
                    self.lastSuccessfulRequestDate = Date()
                }
                
                let dateFormatter = DateFormatter()
                dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
                dateFormatter.timeZone = TimeZone.present
                let readableDate = dateFormatter.string(from: Date())
                
                print(
                    "Location and BLE information despatched efficiently at (readableDate):"
                )
                print(
                    "Latitude: (truncatedLatitude), Longitude: (truncatedLongitude), Timestamp: (timestamp)"
                )
            } else if let httpResponse = response as? HTTPURLResponse {
                print(
                    "Didn't ship location information. Standing code: (httpResponse.statusCode)"
                )
            }
        }
        
        job.resume()
    }
}

My PLIST seems like:

<key>UIBackgroundModes</key>
    <array>
        <string>bluetooth-le</string>
        <string>fetch</string>
        <string>processing</string>
        <string>location</string>
        <string>bluetooth-central</string>
        <string>bluetooth-peripheral</string>
    </array>

I’m nonetheless unable to scan BLE gadgets reliably when the app is within the background.
Glad if somebody may help right here!

Related Articles

Social Media Auto Publish Powered By : XYZScripts.com