Detect GPS in iOS device with CoreTelephony - objective-c

I was looking for a way to detect whether an iOS device has a GPS unit or not, and I ran into this question. I found the last answer very interesting:
CTTelephonyNetworkInfo* netInfo = [[CTTelephonyNetworkInfo alloc] init];
if(netInfo) {
CTCarrier* carrier = [netInfo subscriberCellularProvider];
if([[carrier carrierName] length] <=0) {
//NO operator=>NO 3G and no real GPS
}
}
I was looking for some confirmation as the the validity of this technique/whether or not it is entirely accurate. I don't have enough devices to test it myself.

After doing some digging, I've found this article from Apple which, as a footnote, explains that:
iOS devices without a cellular connection use only Wi-Fi for Location Services (if a Wi-Fi network is available).
GPS is available [only] on iPhone and iPad Wi-Fi + 3G models.
So it seems that detecting a cellular connection is a reliable way to determine whether or not and iOS device has a GPS unit. No cellular connection, no GPS.
Note that iOS supports external GPS units through bluetooth and dock connector. The above methods will tell whether the device has a internal GPS but not whether LocationManager will be able to provide GPS driven location updates.

Related

iOS 9.1 BLE Connects and Immediately Disconnects

When I connect to a BLE peripheral using encryption on iOS 9.1 using a iPhone 6S the BLE peripheral is immediately disconnected. I've verified that this works on iOS 8 and 9.02. I've validated that the CBCentralManager didConnect delegate method is being called and a valid CBPeripheral object is being returned.
The BLE peripheral is disconnected immediately. I'm not able to even proceed to discover any services or characteristics before it is disconnected. The CBCentralManager didDisconnect method is called, but no error is returned.
Is anyone else seeing this? This has been working correctly for over a year and like I said it's only happening when using iOS 9.1 on the iPhone 6S. Don't have any other peripherals on 9.1 to test with.
UPDATE
According to Apple the issue is happening because we're not bonding the peripheral with the iPhone. So we've updated the firmware on the peripheral and are now bonding. This is presenting different issues surrounding maintaining bonding pairs between the phone and the peripheral. Our peripheral has a limited amount of memory, so we cannot maintain an infinite amount of bond references. To handle this scenario the peripheral allows for a maximum of 8 BLE bonds to be stored. When it receives a ninth request the first bond is removed from the peripherals list of bonded devices. This presents an issue because the iPhone doesn't know this bond has been removed and when I try to connect with the first device and interact with a characteristic I'm not prompted to pair. Which from my understanding is what kicks off the encryption process.
I'm wondering if there is anyway to remove a bond / device from an iPhones list of bonded devices, other then going into the Bluetooth settings and 'Forgetting' the device.
UPDATE #2
Apple has confirmed that there is NOT a way to remove a bonded peripheral from the 'MY Devices' section within the Bluetooth settings in the application due to security issues.
Based on this the scenario has now turned into the following. The iOS device has a reference to what was once a bonded peripheral under "My Devices" however that peripheral no longer has a reference to the iOS device within it's bonded devices table.
When I attempt to connect the iOS device, which still has the bond reference in it, to the peripheral that no longer has the bond reference we can connect successfully. However, after I connect successfully I discover the characteristic, set it to notify and receive no response and no pairing message. The only solution that fixes this issue is going into 'Settings' and 'Forgetting' the device and then attempting to reconnect.
Has anyone else experience this issue? Suggestions?
Thanks!

iOS 7 Sandbox Game Center Live Matches not matching on Cellular connection

Auto matching works fine on a wifi connection using
[[GKMatchmakerViewController alloc] initWithMatchRequest:matchRequest];
However, when I switch both devices to use the cellular data connection then the match won't get created. It is just stuck looking for match "forever". Any ideas on what would be causing that? Is that as designed from Apple's matchmaking algorithm.

iOS 7 Core Bluetooth Peripheral running in background

What I want is for my iOS device to be advertising a Bluetooth LE service all the time, even when the app isn't running, so that I can have another iOS device scan for it and find it. I have followed Apple's backgrounding instructions here:
https://developer.apple.com/library/ios/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/PerformingCommonPeripheralRoleTasks/PerformingCommonPeripheralRoleTasks.html#//apple_ref/doc/uid/TP40013257-CH4-SW1.
I can get it to advertise in the foreground ok and sometimes in the background but it doesn't stay advertising all the time. If you have it setup to run in the background, shouldn't it start advertising even after a device restart, just like background location services automatically start working after a restart? Are their limitations to the backgrounding that are not listed (or hard to find) in Apple's docs? Does anyone have an example of a Core Bluetooth Peripheral advertising correctly in the background?
Thanks...
Background advertisement is possible if you add the bluetooth-peripheral backgrounding mode to the app's plist. Once you do that, your app will continue to receive the callbacks even if backgrounded.
The advertisement is a tricky beast as Apple implemented several optimizations to reduce the power consumption and these reduce the quality of the advertisement as soon as the app is backgrounded. Namely: the rate is reduced severely, the advertised services are not included and the local name is not included either. Once the app comes back to foreground, these restrictions are invalidated.
In the general case, this kind of backgrounded operation requires the app to be running. With iOS 7 the restoration process has been implemented that allows the OS to act on the app's behalf while it is terminated and restore the app when some transmission or other operation is imminent. This requires you to add the restoration key to the initialization options of the CBPeripheralManager/CBCentralManager. Starting your application once is still required but after that, iOS will continue to act as the BLE facade towards the centrals/peripherals.
UPDATE: I ran a loop on the Apple bluetooth-dev list as well with this question and found that Core Bluetooth managers were declared to be not able to restore after reboot. This is not described in any documentation but probably was mentioned in the WWDC videos. We should file a bug and replicate it to raise Apple's awareness.
The implementation can be founded here:
https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/PerformingCommonPeripheralRoleTasks/PerformingCommonPeripheralRoleTasks.html#//apple_ref/doc/uid/TP40013257-CH4-SW5
Its very simply actually.
<CBPeripheralManagerDelegate>
#property (strong, nonatomic) CBPeripheralManager *peripheralManager;
...
- (NSDictionary*) advertiseNotBeacon {
CBUUID *myCustomServiceUUID = [CBUUID UUIDWithString:#"MY_UUID"];
CBMutableCharacteristic *myCharacteristic = [[CBMutableCharacteristic alloc] initWithType:myCustomServiceUUID
properties:CBCharacteristicPropertyRead | CBCharacteristicPropertyNotify
value:nil permissions:CBAttributePermissionsReadable];
CBMutableService *myService = [[CBMutableService alloc] initWithType:myCustomServiceUUID primary:YES];
myService.characteristics = #[myCharacteristic];
self.peripheralManager.delegate = self;
[self.peripheralManager addService:myService];
return #{CBAdvertisementDataServiceUUIDsKey : #[myService.UUID],
CBAdvertisementDataLocalNameKey: #"MY_NAME"
};
}
...
[self.peripheralManager startAdvertising:[self advertiseNotBeacon]];
Doing this, you will start advertising a peripheral service.
Now, if you want to keep the service running in background, read the documentation in this link: https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/CoreBluetoothBackgroundProcessingForIOSApps/PerformingTasksWhileYourAppIsInTheBackground.html#//apple_ref/doc/uid/TP40013257-CH7-SW1
Quick steps:
Open info.plist
Add a new par key/value
Required background modes
App shares data using CoreBluetooth
Apple says to ctrl + click on any key/value and add a add the following, but exactly the same as explained before.
UIBackgroundModes
bluetooth-peripheral
Dont forgot about the limitations of running the service in background:
The CBCentralManagerScanOptionAllowDuplicatesKey scan option key is ignored, and multiple discoveries of an advertising peripheral are coalesced into a single discovery event.
If all apps that are scanning for peripherals are in the background, the interval at which your central device scans for advertising packets increases. As a result, it may take longer to discover an advertising peripheral.
These changes help minimize radio usage and improve the battery life on your iOS device.

Phonegap GPS in offline mode

I'm creating a quick very simple mobile app using phone gap.
One of the APIs I'm hooking into is the gps. For some reason, the gps is not working while the device has no wifi or cell connection. I'm testing on a nexus10 which should have a gps receiver.
Is there anyway to force the device to use the gps receiver or is a connection a requirement to the GPS API?
Try using:
navigator.geolocation.watchPosition(onSuccess, onError, { enableHighAccuracy: true });
"enableHighAccuracy" tells the Android device to use the GPS receiver rather than rely on Wifi or cell triangulation. An internet connection isn't required to use the GPS receiver.

GameKit (GKSession) : Is there a priority order when GKSession decides to use Wifi or Bluetooth (if both are available)

If I have two iOS devices, both on same WiFi network and both with Bluetooth turned on, and I use GameKit (specifically GKSession) to manually setup a communications channel between them (without using GKPeerPickerController), I cant tell if it is using WiFi or Bluetooth.
Does iOS prioritise one over the other? I'm hoping that it uses Wifi before Bluetooth, but id like to be sure.
If WiFi is available and bluetooth isn't, it uses Wifi, if Bluetooth is available and Wifi isn't, it uses Bluetooth. Im wondering how they're talking if both bluetooth and WiFi are available, which does GameKit choose over the other?
The only way I can see to find this out is by running a packet sniffer on my WiFi and running several tests across different devices. Kinda hoping someone can save me that effort!
Thanks :-)
According to Apple's documentation if you use a GKPeerPickerController to create your GKSession you will be able to select bluetooth or wifi connectivity (see GKPeerPickerConnectionType).
I'm hoping that it uses Wifi before Bluetooth, but id like to be sure.
It seems an internet connections requires a bit of user code (but not bluetooth) so I would guess it defaults to bluetooth to avoid making this requirement mandatory.