Beacons array is empty in didRangeBeaconsInRegion event in ionic ios app - ios

im using ionic native ibeacon library to detect beacons.I can detect beacons with android but when I try in ios ,i always see an empty beacon array.
I tried these things ,but still cant see the beacons in ios (device is iphone 6s plus 11.4.1) (bluetooth service is enabled on device)
I tried both requestWhenInUseAuthorization and also
requestAlwaysAuthorization.
I add NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription key in info.plist
my code is like this, its working on android device
import { Injectable } from '#angular/core';
import { Platform, Events } from 'ionic-angular';
import { IBeacon } from "#ionic-native/ibeacon";
#Injectable()
export class BeaconProvider {
delegate: any;
region: any;
constructor(
public platform: Platform,
public events: Events,
private iBeacon: IBeacon
) {
this.initialise();
}
initialise(): any {
let promise = new Promise((resolve, reject) => {
if (this.platform.is("cordova")) {
this.iBeacon.requestAlwaysAuthorization();
// ALSO try this one too this.iBeacon.requestWhenInUseAuthorization();
this.delegate = this.iBeacon.Delegate();
this.delegate.didRangeBeaconsInRegion().subscribe(
data => {
this.events.publish("didRangeBeaconsInRegion", data);
//console.log("didRangebeacons__" + JSON.stringify(data)); // empty beacons array
},
error => console.error()
);
this.region = this.iBeacon.BeaconRegion("deskBeacon", "e2c56db5-dffb-48d2-b060-d0f5a71096e0");
this.iBeacon
.startRangingBeaconsInRegion(this.region)
.then(
() => {
resolve(true);
},
error => {
console.error("Failed to begin monitoring: ", error);
resolve(false);
}
);
} else {
resolve(false);
}
});
return promise;
}
}
EDIT
My location services are on ,and I'm using same uuid in ios or android (ionic,same code).By the way I tried to make a iphone as beacon transmitter by an app in market,other iphone can see it as beacon.
And here is the screenshot of beacon scope app

A few things to check on iOS:
Go to settings, location, and check if your app has been granted location permission.
Make sure Bluetooth is turned on
Try a 3rd party beacon scanner like Locate Beacon, configure it with your UUID and make sure it can detect your beacon with the same device.
EDIT: A few more steps
Make sure iOS has Location turned on in Settings (the overall setting, not just for your app) Settings -> Privacy -> Location Services
Since you can detect on Android but not iOS, double check the UUID that you see on Android and make sure it matches exactly what you enter on iOS.
If the UUID in the config matches, but it still won't detect, verify that the beacon is actually sending out an iBeacon frame and not AltBeacon or some format iPhone won't see by default. If you use the Beacon Scope app for Android, it will tell you the frame type.

Related

Get Permission to Scan Barcodes on iOS with Flutter

I'm using qr_code_scanner to scan barcodes in my Flutter app and it works fine for Android, but when I try to scan for iOS a pop-up appears and looks like:
I'm using the descriptions Flutter code that looks like the following:
QRView(
key: qrKey,
onQRViewCreated: (controller) => {
controller.scannedDataStream.listen((code) async {
...
})
})
And in my Info.plist file I have the following fields:
<key>io.flutter.embedded_views_preview</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>Camera permission is required to scan QR codes.</string>
However even with these settings set, I can't seem to figure out how to have access to the camera. Thanks for any help.
Update
I followed #EvgeniyTrubilo suggestion and used permission_handler to request permission using the following code:
void getCameraPermission() async {
print(await Permission.camera.status); // prints PermissionStatus.granted
var status = await Permission.camera.status;
if (!status.isGranted) {
final result = await Permission.camera.request();
if (result.isGranted) {
setState(() {
canShowQRScanner = true;
});
} else {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('Please enable camera to scan barcodes')));
Navigator.of(context).pop();
}
} else {
setState(() {
canShowQRScanner = true;
});
}
}
The first time this code was executed it successfully requested permission to access camera, and once permission was granted, the same error showed up. Sequential tries the print statement at the top of the above function said the Permission was granted??
Update 2x
Just realized you can mock the camera in an iOS simulator like you can on Android. I will try this on an actual device and update.
You can use permission_handler. With this, you could ask for camera permission before build QRView. Of course, you should build QRView after camera permission is enabled.
I'm not sure it would be right solution for your issue, but I think that would be an awesome improvement.

Not able to discover CBServices I am advertising

I am creating a custom CBService and adding them to the CBPeripheralManager. I am advertising the service along with the Advertisement data. But I am not able to see the service I created in the Light Blue app. The Light Blue app shows two services which I don't know what they are and I didn't add those. The advertisement data is shown but not the service. Below is my implementation of creating a service and advertising it in Xamarin:
void AdvertiseData()
{
var uui = new CBUUID[] { CBUUID.FromString("E20A39F4-73F5-4BC4-A12F-17D1AD07A961") };
var nsArray = NSArray.FromObjects(uui);
var nsObject = NSObject.FromObject(nsArray);
var manufacturerDataBytes = new byte[6] { 5, 255, 76, 0, 25, 35 };
var advertisementData = new NSDictionary(
CBAdvertisement.DataLocalNameKey, "id1",
CBAdvertisement.DataServiceUUIDsKey, nsObject,
CBAdvertisement.DataManufacturerDataKey, NSData.FromArray(manufacturerDataBytes));
try
{
var newService = new CBMutableService(CBUUID.FromString("1F3A8B1E-4BAA-45DA-B3A4-7A19DDC86305"), false);//1F3A8B1E-4BAA-45DA-B3A4-7A19DDC86305
newService.Characteristics = new CBCharacteristic[]
{
new CBMutableCharacteristic(CBUUID.FromString("906377DD-F782-448C-9A65-796B48E10894"),//906377DD-F782-448C-9A65-796B48E10894
CBCharacteristicProperties.Read, "FooChar",
CBAttributePermissions.Readable),
new CBMutableCharacteristic(CBUUID.FromString("2B77EC99-BE87-45D3-863C-599D5202E4C0"),//2B77EC99-BE87-45D3-863C-599D5202E4C0
CBCharacteristicProperties.Write, null,
CBAttributePermissions.Writeable)
};
cbPeriphMang.AddService(newService);
//cbPeriphMang.RemoveAllServices();
}
catch(Exception ex)
{
}
if (cbPeriphMang.Advertising) cbPeriphMang.StopAdvertising();
cbPeriphMang.StartAdvertising(new StartAdvertisingOptions(){LocalName="alpha", ServicesUUID = new CBUUID[] { CBUUID.FromString("1F3A8B1E-4BAA-45DA-B3A4-7A19DDC86305")} } );
}
I assume you are running the LightBlue app on another iOS device. In my experience iOS caches a lot of BLE data including services and characteristics. If the peripheral changes any service or characters (or adds or deletes) then iOS will not see those changes until you restart bluetooth on the iOS device where LightBlue is running. In some cases all I need to do is turn bluetooth off/on but in other cases I had to restart the device completely. If you scan the Apple developer forums you will find other people reporting the same problem and same workaround.

Use Bloothtooth LE while app is in background

I am building an iOS app with Xamarin, with this BLE plugin:
https://github.com/aritchie/bluetoothle
I'm just broadcasting a UUID via BLE, and it works. Here is my code:
var data = new Plugin.BluetoothLE.Server.AdvertisementData
{
LocalName = "MyServer",
};
data.ServiceUuids.Add(new Guid("MY_UUID_HERE"));
await this.server.Start(data);
The only problem is that it stops broadcasting once I put the app in the background. And resumes again when I open the app again.
How can I let it continue to broadcast once it's in the background? I read the documentation here:
https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/CoreBluetoothBackgroundProcessingForIOSApps/PerformingTasksWhileYourAppIsInTheBackground.html
And it says that I have to use the CBCentralManager class to obtain the preservation and restoration feature (so I can keep broadcasting the UUID at all times), but I'm having a hard time translating this to Xamarin/C#.
EDIT
After researching some more, I read that I need to create an instance of CBCentralManager and implement WillRestoreState in the delegate. I did this in the AppDelegate:
[Register("AppDelegate")]
public class AppDelegate : MvxApplicationDelegate, ICBCentralManagerDelegate
{
private IGattServer server = CrossBleAdapter.Current.CreateGattServer();
private CBCentralManager cbCentralManager;
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
// irrelevant code...
this.Ble();
return true;
}
private async Task Ble()
{
try
{
await Task.Delay(5000); // wait for it to finish initializing so I can access BLE (it crashes otherwise)
var options = new CBCentralInitOptions();
options.RestoreIdentifier = "myRestoreIndentifier";
this.cbCentralManager = new CBCentralManager(this,null,options);
var data = new Plugin.BluetoothLE.Server.AdvertisementData
{
LocalName = "MyServer",
};
data.ServiceUuids.Add(new Guid("MY_UUID_HERE"));
await this.server.Start(data);
}
catch (Exception e)
{
}
}
public void UpdatedState(CBCentralManager central)
{
//throw new NotImplementedException();
}
[Export("centralManager:willRestoreState:")]
public void WillRestoreState(CBCentralManager central, NSDictionary dict)
{
//never gets called
}
But it didn't make a difference for me. And the WillRestoreState method never gets called... I don't mind using a different plugin/library if I have to at this point...
EDIT 2
I just realized that the app is still broadcasting while it is in the background, I just don't see the service UUID anymore (in the web portal of the beacon that I'm testing with), I only see the phone's identifier.
After doing tons of research, I found that it is simply an iOS restriction - you can not broadcast the UUID of a BLE service while your app is in the background. Background work is very restrictive in iOS.
EDIT to include Paulw11 comment (which is true):
You can advertise a service, but it is advertised in a way that only another iOS device that is specifically scanning for that service UUID can see.
Although you can not broadcast the UUID of a BLE service while your iOS app is in the background, for anyone trying to do something similar, you should look into iBeacon. It's Apple's protocol for letting iOS apps do bluetooth stuff while it's in the background.

swift nearby api doesn't find beacons

I want to use my swift ios app to scan for beacons using google nearby api (not iBeacon api)
I saw Google developer doc, and I took the git sample from the same site.
Here is my code
I have installed the app on a real ios device for the first time
but the found and lost handlers are never called.
I doubled checked the bundle id, the public ios API key (same google console project of the beacon attachment)
but it still doesn't work near a working and registered beacon.
I also have an android app which succeeds in scanning the same beacon.
What else can I check?
I'm missing the "Strategy" piece of code in my swift code.
How can I add this? Why is this missing in the github example?
GNSStrategy *beaconStrategy = [GNSStrategy
strategyWithParamsBlock:^(GNSStrategyParams *params) {
params.includeBLEBeacons = YES;
}];
GNSSubscriptionParams *beaconParams = [GNSSubscriptionParams
paramsWithMessageNamespace:#"com.mycompany.mybeaconservice"
type:#"mybeacontype"
strategy:beaconStrategy];
_beaconSubscription = [_messageManager subscriptionWithParams:beaconParams
messageFoundHandler:myMessageFoundHandler
messageLostHandler:myMessageLostHandler];
in my code:
func startScanning() {
if let messageMgr = self.messageMgr {
// Subscribe to messages from nearby devices and display them in the message view.
subscription = messageMgr.subscriptionWithMessageFoundHandler({[unowned self] (message: GNSMessage!) -> Void in
self.mainViewController.location_text.text = (String(data: message.content, encoding:NSUTF8StringEncoding))
self.mainViewController.startRefreshTimer()
},
messageLostHandler: {[unowned self](message: GNSMessage!) -> Void in
if (self.mainViewController.userState.enrollState == EnrollState.confirmPosition)
{
self.mainViewController.stopRefreshTimer()
self.mainViewController.enrollButtonManager.setSearchingForBeaconsBtn()
}
})
}
}
You'll need to add the GNSStrategy to your subscription, which lets you enable beacon scanning. Try this:
let params: GNSSubscriptionParams = GNSSubscriptionParams.init(strategy:
GNSStrategy.init(paramsBlock: { (params: GNSStrategyParams!) -> Void in
params.includeBLEBeacons = true;
}))
subscription = messageMgr.subscriptionWithParams(params,
messageFoundHandler:{[unowned self] (message: GNSMessage!) -> Void in
self.mainViewController.location_text.text = (String(data: message.content, encoding:NSUTF8StringEncoding))
self.mainViewController.startRefreshTimer()
},
messageLostHandler: {[unowned self](message: GNSMessage!) -> Void in
if (self.mainViewController.userState.enrollState == EnrollState.confirmPosition) {
self.mainViewController.stopRefreshTimer()
self.mainViewController.enrollButtonManager.setSearchingForBeaconsBtn()
}
})
Beacon scanning is off by default because iOS presents the user with the location permission dialog when beacon scanning is first turned on, and this is unacceptable for apps that use the Nearby library but don't scan for beacons.
Thanks for the feedback about the github example not showing how to scan for beacons. I'll see about adding it.

Xamarin.iOS CoreBluetooth/External Accesory issue

I've looking here on Forums, on the monotouch samples GIT hub, and never found a really functional sample to use CoreBluetooth in order to achieve the following:
1.Check if is there a device that match a criteria(by name or some identifier of the device) paired and connected
2.If paired but not connected, try connect to it
3.If connection fails, then show a list of the bluetooth devices that matches the criterias on topic 1 so the user can select and connect to it
Note: The device I'm trying to connect uses SPP but is Apple MFi certified. It is a credit card reader over bluetooth and some of then even implement ExternalAccessory protocols
The CoreBluetooth samples page is empty http://developer.xamarin.com/samples/ios/CoreBluetooth/
I've trying this pretty simple sample that never get the events called after the scan:
public static class BTHelper
{
private static CBCentralManager manager;
private static CBUUID UUID;
static BTHelper()
{
manager =
manager.DiscoveredPeripheral += OnDiscovery;
manager.ConnectedPeripheral += OnConnected;
manager.DisconnectedPeripheral += OnDisconnected;
UUID = CBUUID.FromString("00001101-0000-1000-8000-00805F9B34FB");
}
public static void CheckBluetooth()
{
manager.ScanForPeripherals(new[] { UUID });
}
static void OnDisconnected(object sender, CBPeripheralErrorEventArgs e)
{
Console.WriteLine("Disconnected - " + e.Peripheral.Name);
}
static void OnConnected(object sender, CBPeripheralEventArgs e)
{
Console.WriteLine("Connected - " + e.Peripheral.Name);
}
static void OnDiscovery(object sender, CBDiscoveredPeripheralEventArgs e)
{
Console.WriteLine("Found - " + e.Peripheral.Name);
}
}
Can anyone help? I've being really tired of googling and looking of many questions on SO with no real answer.
#XamarinTeam, you guys should provide a sample on how to use it... We are lost without reference...
Thank, really appreciate any help...
Gutemberg
It seems like you are looking at wrong documents.Core Bluetooth only allows you to communicate with Bluetooth Low Energy (BLE) devices using the GATT profile. you can not scan SPP device with corebluetooth.
For your MFI device, you need to check External Accessory framework , It allows communication with 'legacy' Bluetooth devices using profiles such as the Serial Port Protocol (SPP).
To answer your question:
: 1.Check if is there a device that match a criteria(by name or some identifier of the device) paired and connected
You can use showBluetoothAccessoryPicker function of EAAccessoryManager to get list of Available devices, read more here
2.If paired but not connected, try connect to it
There is not any documented way to check for this. You can not initiate connect from app without showBluetoothAccessoryPicker . You can monitor for
EAAccessoryDidConnect notification. if this method is not called, and showbluetoothaccessorypicker 's complition get called, your device is not connected.
3.If connection fails, then show a list of the bluetooth devices that matches the criterias on topic 1 so the user can select and connect to it
1)
After completion of showbluetoothaccessorypicker You can check in ConnectedAccessories . If its not avaiable, call showbluetoothaccessorypicker to display list of accessories.
Sample code for using External Accessory framework in your code
EAAccessoryManager manager= EAAccessoryManager.SharedAccessoryManager;
var allaccessorries= manager.ConnectedAccessories;
foreach(var accessory in allaccessorries)
{
yourlable.Text = "find accessory";
Console.WriteLine(accessory.ToString());
Console.WriteLine(accessory.Name);
var protocol = "com.Yourprotocol.name";
if(accessory.ProtocolStrings.Where(s => s == protocol).Any())
{
yourlable.Text = "Accessory found";
//start session
var session = new EASession(accessory, protocol);
var outputStream = session.OutputStream;
outputStream.Delegate = new MyOutputStreamDelegate(yourlable);
outputStream.Schedule(NSRunLoop.Current, "kCFRunLoopDefaultMode");
outputStream.Open();
}
}
and
public class MyOutputStreamDelegate : NSStreamDelegate
{
UILabel label;
bool hasWritten = false;
public MyOutputStreamDelegate(UILabel label)
{
this.label = label;
}
public override void HandleEvent(NSStream theStream, NSStreamEvent streamEvent)
{
//write code to handle stream.
}
}
There is not any perticular demo for using Exeternal Accessory framework,
but You can check this sample code for understanding how it works.:
Whole Project
AccessoryBrowser class

Resources