I am using Apple core bluetooth example .The peripheral is running in foreground in one iphone device.I am running cbcentral client application in one device.It is pairing well when both application in foreground condition.My need is when I run client cbcentral client in background , that delegate methods are not called in which I have mentioned local notification .the notification is not coming in background mode.
Can I use NSOperation for running bluetooth delegate methods as we do NSUrlConnection? Will it work in latest iOS version? I checked it, but it is not working.
The code:
-(void) peripheral:(CBPeripheral *)aPeripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
..............
...............
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
localNotif.fireDate = [itemDate dateByAddingTimeInterval:-(minutesBefore*60)];
localNotif.timeZone = [NSTimeZone defaultTimeZone];
localNotif.alertBody = #"hi";
localNotif.alertAction = NSLocalizedString(#"View Details", nil);
localNotif.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
..............
}
I believe what you are looking for are the core bluetooth UIBackgroundModes here.
Also, you may want to look at Core Bluetooth and backgrounding: Detection of a device and triggering an action, even after being days in background mode? and What exactly can CoreBluetooth applications do whilst in the background?
The core bluetooth background modes work in iOS 5 or later.
Related
I would like to connect 2 devices using multipeer connectivity framework where one of those devices is running the app in the background, just like Firechat does (I can't confirm this is working, I have installed it on an iPhone 5S and 4, but they just can't find each other - but I have read somewhere this works).
What's the best way to achieve this?
I'm using the following two methods from an example code:
-(void)setupPeerAndSessionWithDisplayName:(NSString *)displayName{
_peerID = [[MCPeerID alloc] initWithDisplayName:displayName];
_session = [[MCSession alloc] initWithPeer:_peerID];
_session.delegate = self;
}
-(void)setupMCBrowser{
_browser = [[MCBrowserViewController alloc] initWithServiceType:#"chat-files" session:_session];
}
-(void)advertiseSelf:(BOOL)shouldAdvertise{
if (shouldAdvertise) {
_advertiser = [[MCAdvertiserAssistant alloc] initWithServiceType:#"chat-files"
discoveryInfo:nil
session:_session];
[_advertiser start];
}
else{
[_advertiser stop];
_advertiser = nil;
}
}
When I'm running the app in the foreground, it finds the other device flawlessly and also connects. But if I put one of the apps into background, the background-app-device is no longer visible.
I have already read this: Can I have a multipeer connectivity session run in the background? - But I can't believe this is not possible without any workaround.
FYI, my app is also using background location updates, if that's relevant...
Thanks!
EDIT:
Is there some other way of doing this?? Basically I just want to send a message to the other device (to the one who's app is running in the background) - since Multipeer connectivity doesn't work in the background, can I connect via bluetooth directly for example?
Apple have confirmed that "Multipeer Connectivity does not function in the background". The reason is that if your app is suspended then its socket resources can be reclaimed and everything falls apart.
However, it is possible to monitor iBeacons when in the background. Essentially, you set your app up to monitor proximity to a beacon with a specific id. Then, your app that is in the foreground becomes a beacon with that same id. This causes the following app delegate method to be called on the app in the background:
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region {
UILocalNotification *notification = [[UILocalNotification alloc] init];
if(state == CLRegionStateInside) {
notification.alertBody = NSLocalizedString(#"A nearby app would like to connect", #"");
} else {
return;
}
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
Here you issue a local notification that, when tapped on by the user, will bring your app into the foreground, at which point you can start advertising for a peer-to-peer connection.
My iOS 7 app is generating local notifications in a method called within an NSOperationQueue block. The notifications are appearing in the Notification Center, but they are not showing a banner at the top of the screen. The notifications are being generated while the app is in the background.
I've tried everything I can think of, and done considerable Google searching, but I still can't get the banners to display.
Here is the code that builds and schedules the notification:
// In the most recent case, I have verified that
// alertText = Why not work? and alertAction = View
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.alertBody = alertText;
localNotification.alertAction = alertAction;
localNotification.alertLaunchImage = launchImage;
UIApplication *application = [UIApplication sharedApplication];
application.applicationIconBadgeNumber++;
localNotification.applicationIconBadgeNumber = application.applicationIconBadgeNumber;
[self performSelectorOnMainThread:#selector(scheduleNotification:)
withObject:localNotification waitUntilDone:NO];
}
- (void)scheduleNotification: (id)notification
{
UILocalNotification *localNotification = (UILocalNotification *)notification;
// Schedule it with the app
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
I have checked the notification settings for my app, and they are:
Alert Style: Banners
Badge App Icon: On
Sounds: Off
Show in Notification Center: On
Include: 5 Recent Items
Show on Lock Screen: On
The bug was actually in a different part of my code. I was generating the notification in a background thread, and the thread was canceled before the notification went out.
If your app is running you can't have this banners (unless you create your own).
A solution could be:
When the app is running, Notification are handle by
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
Then you can use this project (that I use and which is really good) : TSMessages to create something similar as your banner.
Hope that will help...
So after much trial and error and reading Apples documents and SO threads I thought I had my didEnterRegion working properly.
This is what I finished up with...
- (void)locationManager:(LocationManager *)locationManager didEnterRegion:(CLRegion *)region{
NSLog(#"Location manager did enter region called");
[self.locationManager stopUpdatingLocation];
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateActive) {
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
UILocalNotification *localNotification = [[UILocalNotification alloc]init];
localNotification.alertBody = #"You are about to arrive";
localNotification.alertAction = #"Open";
localNotification.applicationIconBadgeNumber = [[UIApplication sharedApplication]applicationIconBadgeNumber]+1;
[[UIApplication sharedApplication]presentLocalNotificationNow:localNotification];
} else {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"App Running in foreground notification fired");
[self setupAlarmTriggeredView];
//vibrate device
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
});
}
}
As you can see it does a simple check to see if the app is active, if not it sets up a UILocalNotification which it presents immediately, if it is it just vibrates and changes the view.
When testing on the simulator using a GPX file to move the location over the boundary, both scenarios work perfectly. However when testing the app out and about, when the app is in the background the notification doesn't seem to fire until you wake the device. ie. you can cross the boundary and nothing happens, then when you unlock the device, boom, it vibrates and the view is changed accordingly. (if the app is not foremost when you unlock this doesn't happen until you re-launch the app).
Should I be doing this setup in appDidFinishLaunchingWithOptions? In my testing both that method and didRecieveLocalNotification: in the app delegate don't get called until 'after' the notification has been fired AND the user has re-launched the app by actioning the notification (at which point you can check the for the launch key in the options array), this doesn't seem to be of any use for the initial firing of the notification as part of didEnterRegion. At the moment I have no code in either of these methods.
As far as I'm aware I don't need to be doing any background location updates for didEnterRegion, this is handled automatically by iOS.
I am having difficulties getting this to work for when the app is not running. I have locationManager:didRangeBeacons:inRegion: implemented and it is called when the app is running in the foreground or background, however it doesn't seem to do anything when I quit the app and lock the screen. The location services icon goes away and I never know that I entered a beacon range. Should the LocalNotification still work?
I have Location updates and Uses Bluetooth LE accessories selected in Background Modes (XCode 5) I didn't think I needed them.
Any help greatly appreciated.
-(void)watchForEvents { // this is called from application:didFinishLaunchingWithOptions
id class = NSClassFromString(#"CLBeaconRegion");
if (!class) {
return;
}
CLBeaconRegion * rflBeacon = [[CLBeaconRegion alloc] initWithProximityUUID:kBeaconUUID identifier:kBeaconString];
rflBeacon.notifyOnEntry = YES;
rflBeacon.notifyOnExit = NO;
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[self.locationManager startRangingBeaconsInRegion:rflBeacon];
[self.locationManager startMonitoringForRegion:rflBeacon];
}
-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
if (beacons.count == 0 || eventRanged) { // breakpoint set here for testing
return;
}
eventRanged = YES;
if (backgroundMode) { // this is set in the EnterBackground/Foreground delegate calls
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = [NSString stringWithFormat:#"Welcome to the %# event.",region.identifier];
notification.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
// normal processing here...
}
Monitoring can launch an app that isn't running. Ranging cannot.
The key to having monitoring launch your app is to set this poorly documented flag on your CLBeaconRegion: region.notifyEntryStateOnDisplay = YES;
This can launch your app on a region transition even after completely rebooting your phone. But there are a couple of caveats:
Your app launches into the background only for a few seconds. (Try adding NSLog statements to applicationDidEnterBackground and other methods in your AppDelegate to see what is going on.)
iOS can take its own sweet time to decide you entered a CLBeaconRegion. I have seen it take up to four minutes.
As far as ranging goes, even though you can't have ranging wake up your app, you can make your app do both monitoring and ranging simultaneously. If monitoring wakes up your app and puts it into the background for a few seconds, ranging callbacks start up immediately. This gives you a chance to do any quick ranging actions while your app is still running.
EDIT: Further investigation proves that notifyEntryStateOnDisplay has no effect on background monitoring, so the above should work regardless of whether you have this flag. See this detailed explanation and discussion of delays you may experience
Code for iOS 9 to range beacons in the background, by using Location Updates:
Open Project Settings -> Capabilities -> Background Modes -> Toggle Location Updates and Uses Bluetooth LE accessories to ON.
Create a CLLocationManager, request Always monitoring authorization (don't forget to add the Application does not run in background to NO and NSLocationAlwaysUsageDescription in the app's info.plist) and set the following properties:
locationManager!.delegate = self
locationManager!.pausesLocationUpdatesAutomatically = false
locationManager!.allowsBackgroundLocationUpdates = true
Start ranging for beacons and monitoring region:
locationManager!.startMonitoringForRegion(yourBeaconRegion)
locationManager!.startRangingBeaconsInRegion(yourBeaconRegion)
locationManager!.startUpdatingLocation()
// Optionally for notifications
UIApplication.sharedApplication().registerUserNotificationSettings(
UIUserNotificationSettings(forTypes: .Alert, categories: nil))
Implement the CLLocationManagerDelegate and in your didEnterRegion send both startRangingBeaconsInRegion() and startUpdatingLocation() messages (optionally send the notification as well) and set the stopRangingBeaconsInRegion() and stopUpdatingLocation() in didExitRegion
Be aware that this solution works but it is not recommended by Apple due to battery consumption and customer privacy!
More here: https://community.estimote.com/hc/en-us/articles/203914068-Is-it-possible-to-use-beacon-ranging-in-the-background-
Here is the process you need to follow to range in background:
For any CLBeaconRegion always keep monitoring on, in background or foreground and keep notifyEntryStateOnDisplay = YES
notifyEntryStateOnDisplay calls locationManager:didDetermineState:forRegion: in background, so implement this delegate call...
...like this:
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region{
if (state == CLRegionStateInside) {
//Start Ranging
[manager startRangingBeaconsInRegion:region];
}
else{
//Stop Ranging
[manager stopRangingBeaconsInRegion:region];
}
}
I hope this helps.
You are doing two separate operations here - 'ranging' beacons and monitoring for a region. You can monitor for a region in the background, but not range beacons.
Therefore, your implementation of locationManager:didRangeBeacons:inRegion: won't get called in the background. Instead, your call to startMonitoringForRegion will result in one / some of the following methods being called:
– locationManager:didEnterRegion:
– locationManager:didExitRegion:
– locationManager:didDetermineState:forRegion:
These will get called in the background. You can at that point trigger a local notification, as in your original code.
Your app should currently wake up if you're just wanting to be notified when you enter a beacon region. The only background restriction I know of concerns actually hosting an iBeacon on an iOS device. In that case, the app would need to be physically open in the foreground. For that situation, you'd be better off just doing the straight CoreBluetooth CBPeripheralManager implementation. That way you'd have some advertising abilities in the background.
When my iOS app exits, it registers a series of local notifications, which update the badge number at specific times. The local notifications do not bring up a popup, they simply update the badge. On my old iPod touch which does not support multitasking, this works perfectly. However, on my multitasking enabled devices, I am experiencing a very strange bug: when I have "exited" the app (i.e. it is still running in the background, but I am doing something else), the local notifications are not firing. Is there any reason why the local notifications would not fire when the app is in the background?
The code to create the local notifications runs in a loop (I create a bunch of them):
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.applicationIconBadgeNumber = totalCount; // a number generated earlier in the code
localNotification.timeZone = [NSTimeZone defaultTimeZone];
localNotification.fireDate = endDate; // a date generated earlier
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
[localNotification release];
And also I have created the following function in my app delegate, which tells me how many notifications are set up before the app enters the background:
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(#"# Notifications: %d", [[[UIApplication sharedApplication] scheduledLocalNotifications] count]);
}
The app constantly tells me that there are 64 notifications (the number that should be set up) when it enters the background.
Check the following from Apple's developers docs:
"Each application on a device is limited to 64 scheduled local notifications. The system discards scheduled notifications in excess of this limit, keeping only the 64 notifications that will fire the soonest. Recurring notifications are treated as a single notification."
Could you problem be related to the number of notifications been scheduled?
You can find further information in http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/WhatAreRemoteNotif.html
Well #Jason you need to set the alertBody of the local notification atleast to show an alert view, thats all there is to it.
Also if you dont wont to show the view option in the alert box then set the hasAction attribute to NO.
Swift Version:
func applicationDidEnterBackground(application: UIApplication) {
let notification = UILocalNotification()
notification.alertBody = "App has been entered in background"
notification.alertAction = "open"
notification.fireDate = NSDate()
notification.soundName = UILocalNotificationDefaultSoundName
UIApplication.sharedApplication().scheduleLocalNotification(notification)
}