0

I managed to create an app which turns the phone into a beacon emitter.

It works fine on xcode (Swift) and phonegap .

The next step for me is making it work in the background as a beacon (I'm developing for iOS 10).

I found similar questions to this online but all of them were out of date ie old ios , objective-C and outdate plug-ins

would really appreciate it if someone can provide me with a working example or guide me on what to change in my code for both an xcode s(wift) and phonegap

Xcode (Swift 3)

import UIKit
import CoreLocation
import CoreBluetooth

class ViewController: UIViewController,CLLocationManagerDelegate,CBPeripheralManagerDelegate,UITextFieldDelegate{

    var locationManager = CLLocationManager()
    var startLocation: CLLocation!
    var localBeacon: CLBeaconRegion!
    var beaconPeripheralData: NSDictionary!
    var peripheralManager: CBPeripheralManager!
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        locationManager.delegate = self

        locationManager.requestAlwaysAuthorization()

        bleSwitch.addTarget(self, action: #selector(ViewController.bleSwitchUpdated(_:)), for: UIControlEvents.valueChanged)

        major.delegate=self
        minor.delegate=self
        txPower.delegate=self

    }

    @IBOutlet weak var minor: UITextField!
    @IBOutlet weak var major: UITextField!
    @IBOutlet weak var txPower: UITextField!
    @IBOutlet weak var bleSwitch: UISwitch!
    @IBOutlet weak var bleView: UIView!
    @IBAction func bleSwitchUpdated(_ sender: UISwitch) {
        if bleSwitch.isOn {
            initLocalBeacon()
            bleView.isHidden = false;
        //bleSwitch.setOn(true, animated: false)
        }
        else {
        stopLocalBeacon()
            bleView.isHidden = true;
        }
    }

    func initLocalBeacon() {
        if localBeacon != nil {
            stopLocalBeacon()
        }

        let localBeaconUUID = "5A4BCFCE-174E-4BAC-A814-092E77F6B7E5"
        let localBeaconMajor: CLBeaconMajorValue = UInt16(major.text!)!
        let localBeaconMinor: CLBeaconMinorValue = UInt16(minor.text!)!

        let uuid = UUID(uuidString: localBeaconUUID)!
        localBeacon = CLBeaconRegion(proximityUUID: uuid, major: localBeaconMajor, minor: localBeaconMinor, identifier: "Your private identifer here")

        beaconPeripheralData = localBeacon.peripheralData(withMeasuredPower: -59)
        peripheralManager = CBPeripheralManager(delegate: self, queue: nil, options: nil)
    }

    func stopLocalBeacon() {
        peripheralManager.stopAdvertising()
        peripheralManager = nil
        beaconPeripheralData = nil
        localBeacon = nil
    }

    func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
        if peripheral.state == .poweredOn {
            peripheralManager.startAdvertising(beaconPeripheralData as! [String: AnyObject]!)
        } else if peripheral.state == .poweredOff {
            peripheralManager.stopAdvertising()
        }
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true

    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.view.endEditing(true)
        if bleSwitch.isOn == true{
        stopLocalBeacon()
            initLocalBeacon()
        }
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

Phonegap

    var app = (function()
{

           // Application object.
    var app = {};

    // Background flag.
    var mAppInBackground = false;

        app.initialize = function()
    {
        document.addEventListener('deviceready', onDeviceReady, false);
        document.addEventListener('pause', onAppToBackground, false);
        document.addEventListener('resume', onAppToForeground, false);
    };

    function onDeviceReady()
    {
        startMonitoringAndRanging();
        startNearestBeaconDisplayTimer();
        displayRegionEvents();

    }

    function onAppToBackground()
    {
        mAppInBackground = true;
        stopNearestBeaconDisplayTimer();
    }

    function onAppToForeground()
    {
        mAppInBackground = false;
        startNearestBeaconDisplayTimer();
        displayRegionEvents();
    }


    function startMonitoringAndRanging()
    {

        // Request permission from user to access location info.
        cordova.plugins.locationManager.requestAlwaysAuthorization();

           BleconOn();

    }


           function BleconOn(){
           var uuid = '00000000-0000-0000-0000-000000000000';
           var identifier = 'advertisedBeacon';
           var minor = 2000;
           var major = 5;
           var beaconRegion = new cordova.plugins.locationManager.BeaconRegion(identifier, uuid, major, minor);

           var delegate = new cordova.plugins.locationManager.Delegate();


           delegate.peripheralManagerDidStartAdvertising = function(pluginResult) {
           console.log('peripheralManagerDidStartAdvertising: '+ JSON.stringify(pluginResult.region));
           };
           // Event when bluetooth transmission state changes
           // If 'state' is not set to BluetoothManagerStatePoweredOn when advertising cannot start
           delegate.peripheralManagerDidUpdateState = function(pluginResult) {
           console.log('peripheralManagerDidUpdateState: '+ pluginResult.state);
           };

           cordova.plugins.locationManager.setDelegate(delegate);

           // Verify the platform supports transmitting as a beacon
           cordova.plugins.locationManager.isAdvertisingAvailable()
           .then(function(isSupported){

                 if (isSupported) {
                 cordova.plugins.locationManager.startAdvertising(beaconRegion)
                 .fail(conole.error)
                 .done();
                 } else {
                 console.log("Advertising not supported");
                 }
                 })
           .fail(function(e) { console.error(e); })
           .done();
           }

           function BleconOff(){

           cordova.plugins.locationManager.stopAdvertising()
           .fail(console.error)
           .done();
           }



    return app;

})();

app.initialize();

for both codes , the location updates and background fetch are checked in the background capabilities for the target. Also , Privacy - Location Always Usage Description is added on both playlists

nero
  • 37
  • 8

1 Answers1

2

Unfortunately, Apple blocks the ability to transmit as a beacon in the background on iOS. All CoreBluetooth standard advertisements are actually blocked, because advertising slots are a limited resource and multiple apps in the background would exhaust this resource quickly.

Apple does have a proprietary backup system for advertising Bluetooth services in the background that uses a special overflow area to include the service identifiers. But while this scheme works for advertising Bluetooth services in the background, it does not work for advertising beacons, because it breaks the format.

Bottom line: background beacon advertising on iOS just does not work. Sorry.

davidgyoung
  • 63,876
  • 14
  • 121
  • 204
  • how about scanning for beacons on the background ?? – nero Dec 08 '16 at 14:03
  • Yes, scanning works in the background. See my answer here: http://stackoverflow.com/questions/19127282/ibeacon-notification-when-the-app-is-not-running – davidgyoung Dec 08 '16 at 15:07
  • Thank you for your response , I downloaded your example from https://github.com/RadiusNetworks/ibeacon-background-demo I did some modifications to it in order to keep on getting RSSI values for a beacon when the app is in the background , and it only gets around 5 RSSI readings when its in background and then after a while it provides another 5 RSSI values . Is there a way to make it keep on ranging ? @davidgyoung – nero Dec 08 '16 at 16:20
  • While monitoring works in the background, ranging is limited in time to around 5-10 seconds after the last region entry/exit event. This is what you are seeing. You can extend this for up to 3 minutes with the technique here: http://developer.radiusnetworks.com/2014/11/13/extending-background-ranging-on-ios.html – davidgyoung Dec 08 '16 at 16:37
  • Thank you , I get it now :) . how long does monitoring last though , because its not instantaneous @davidgyoung – nero Dec 08 '16 at 16:51
  • Monitoring *lasts* forever once you start it. But the callbacks are not instantaneous. If your app gets access to hardware filters, you can get entry callbacks within 1-2 secs of a beacon appearing. If you do not get access to these filters, it can take up to 15 minutes. – davidgyoung Dec 08 '16 at 17:15
  • do you have any example codes on how to do that ? because ideally for my application will need it to identify if a beacon entered a region instantly @davidgyoung – nero Dec 09 '16 at 09:10