I'm writing an IOS application which will record ( over time ) the current battery level of the iPhone device.
I have working code executing within the foreground using UIDeviceBatteryLevelDidChangeNotification, this fires a notification ( which is handled successfully ) when the app in in foreground mode.
Code is as follows:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[UIDevice currentDevice] setBatteryMonitoringEnabled:YES];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(batteryChanged:) name:UIDeviceBatteryLevelDidChangeNotification object:nil];
return YES;
}
- (void)batteryChanged:(NSNotification *)notification
{
NSLog(#"Battery Changed From Observer");
[self UpdateBatteryStatus];
}
-(void)UpdateBatteryStatus
{
UIDevice *myDevice = [UIDevice currentDevice];
double batLeft = (float)[myDevice batteryLevel] * 100;
NSLog(#"Battery level: %f", batLeft);
// Do some stuff related to battery status
}
This executes ( output via log proves it ) every 1% increment ( or decrement ) on the battery status on the phone, which works exactly as expected.
When the app is moved to the background however, the events stop firing. Upon moving the app back to the foreground, the events fire ( it's almost like they are queued up until it reaches the foreground ).
I have searched stack overflow, and most answers are all the same, basically this is a background-mode permissions problem. So I ticked all the boxes:
I added everything just to make sure it wasn't a background persmission problem. Unfortunately, even after a clean, and rebuild ( also a shutdown of Xcode, and re-plugin of iPhone ) the events still do not fire.
Please bear in mind, I have no intention of releasing this to the app store, I'm aware that faking background-mode use cases is a quick and easy rejection. But I would like to get it working for personal usage.
Edit: ios target 11.3
Your app won't get NSNotificationCenter notifications while being in background/suspended. What you could try is to use fetch background mode to mimic remote downloads. Note however downsides of this approach - system itself will decide when to call the callback.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
return YES;
}
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
UIDevice *myDevice = [UIDevice currentDevice];
double batLeft = (float)[myDevice batteryLevel] * 100;
NSLog(#"Battery level: %f", batLeft);
completionHandler(UIBackgroundFetchResultNewData);
}
If you as stated do not plan to release your app in AppStore you can try another hacks with background modes like infinite playing a muted sound in background.
Related
I have implemented local notification for checking battery status. If the battery level is drop by 1% then local notification is received.This works for both i.e. app is in foreground or background for iOS version below 9.
When I update device OS to iOS 9,then I received local notification in foreground but unable to receive notification in background of the Application.
Following is the code which are used to implement.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Enable monitoring of battery status
**[[UIDevice currentDevice] setBatteryMonitoringEnabled:YES];**
// Request to be notified when battery charge or state changes
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(checkBatteryStatus) name:UIDeviceBatteryLevelDidChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(checkBatteryStatus) name:UIDeviceBatteryStateDidChangeNotification object:nil];
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Request to be notified when battery charge or state changes
[[UIDevice currentDevice] setBatteryMonitoringEnabled:YES];
**[[NSNotificationCenter defaultCenter] postNotificationName:UIDeviceBatteryLevelDidChangeNotification object:nil userInfo:nil];**
**[[NSNotificationCenter defaultCenter] postNotificationName:UIDeviceBatteryStateDidChangeNotification object:nil userInfo:nil];**
}
- (void)checkBatteryStatus
{
notifyAlarm = [[UILocalNotification alloc] init];
notifyAlarm.alertBody = #“battery alert";
notifyAlarm.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] presentLocalNotificationNow:notifyAlarm];
[self displayNotification];
}
In displayNotification method we display notification.
I also enable background mode of the app i.e. shown in screenshot.
enter image description here
Any help would be appreciated. Thanks in advance.
You have enabled background fetch mode, which means that your app can receive remote notifications and perform network requests while in the background.
Without using questionable and App Store rejectable methods, it is not possible to do what you want. Apple specifically doesn't want you to be able to run your application in the background unless you have one of their approved use cases.
After some googling, I can understand that the "proximity sensor" which is used to on/off screen when the device is away/near from the user. I watched this video (watch from 30th sec) and surprised about this cool stuff. I want to implement it in my app.
But I come to know that there is no public API is available that can protect the screen lock when proximityMonitoringEnabled is YES. Then how can the above app did this?
For clear understanding, I'm copying some code.
Enable the proximity sensor:
[[UIDevice currentDevice] setProximityMonitoringEnabled:YES];
Setup an observer for sensor change:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(sensorStateMonitor:) name:#"UIDeviceProximityStateDidChangeNotification" object:nil];
Finally you can find the state of proximity sensor from this method:
- (void)sensorStateMonitor:(NSNotificationCenter *)notification
{
if ([[UIDevice currentDevice] proximityState] == YES)
{
NSLog(#"Device is close to user.");
}
else
{
NSLog(#"Device is not closer to user.");
}
}
Question:
I want to show some view when the "Device is close to user" state was called. And want to remove the view if "Device is not closer to user" state was called.
So I added a view and removed inside the sensorStateMonitor: method. But the view was visible only for some fraction of seconds and the screen goes off.
Can I prevent the screen from auto off?
Just confused!!
The screen lock can be enabled/disabled.
[UIApplication sharedApplication].idleTimerDisabled = YES;
I want to get battery usage data from my iPhone. I used UIDevice currentDevice.batteryLevel code, but its returning -1.0000 in NSLog value.
Anyone please help me?
One more question, can I able to fetch other app battery usages in my app?
First, you must enable batteryStatus notification (in appDelegate.m for instance):
- (void)applicationDidBecomeActive:(UIApplication *)application {
...
[[UIDevice currentDevice] setBatteryMonitoringEnabled:YES];
// Now, the system will post UIDeviceBatteryStateDidChangeNotification notification when batteryStatus (or connection) will change
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(batteryStatusDidChange:) name:UIDeviceBatteryStateDidChangeNotification object:nil];
...
}
Then, you can check battery level calling:
[UIDevice currentDevice].batteryLevel;
that will return you a value between 0.0 (0% chareg) and 1.0 (100% charge).
Without calling first setBatteryMonitoringEnabled, battery state will return UIDeviceBatteryStateUnknown and the value of this property is –1.0. (from docs).
Good evening everyone !
I have a simple question : How to enable [CLLocationManager startUpdateLocation] when I receive a correct push notification using didReceiveRemoteNotification:fetch on iOS 7 ?
Right now, I have :
- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
if ([userInfo objectForKey:#"aps"] && [[userInfo objectForKey:#"aps"] objectForKey:#"content-available"])
{
if ([userInfo objectForKey:#"update-location"])
{
[self performSelectorInBackground:#selector(handleLocationNotificationPush:) withObject:completionHandler];
}
if ([userInfo objectForKey:#"update-sensors"])
{
}
}
}
-(void)handleLocationNotificationPush:(void (^)(UIBackgroundFetchResult))completionHandler
{
[CLController.locMgr startUpdatingLocation];
++nbPushReceive;
[self.pushLockForLocation lock];
if ([self.pushLockForLocation waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:25]] == TRUE && self.lastKnownLocation != nil)
{
[self.pushLockForLocation unlock];
// Send my new location to server using HTTP request
[self sendLocationForPushUpdates:self.lastKnownLocation fetchCompletionHandler:completionHandler];
if (![UIApplication sharedApplication].applicationState == UIApplicationStateActive)
// it stops location updates
[self stopAllLocationUpdates];
return;
}
// In case we didn't receive any new position during 25 secondes
[self.pushLockForLocation unlock];
if (![UIApplication sharedApplication].applicationState == UIApplicationStateActive)
{
[self stopAllLocationUpdates];
}
completionHandler(UIBackgroundFetchResultNoData);
}
- (void)locationUpdate:(CLLocation *)location
{
NSLog(#" *** LocationContrller - LocationUpdate location");
self.lastKnownLocation = location;
if (location.horizontalAccuracy < 500)
{
[self.pushLockForLocation signal];
}
}
Of course, my CLController delegate is the same class (location updates work when application is in foreground). In my plist.file, I have the "Remote notifications" checked.
I am missing something ?
Thanks for your help ! :D
This could be related to the fact that in iOS7 enabling Location Services whilst in the background does not give you unlimited background processing time as it did in previous iOS version. Check out the WWDC 2013 What’s New in Core Location video at around 5 minutes 30. Therefore your app gets suspended again around 30 seconds after receiving the push notification.
I have a similar problem which as yet I've not found the solution to. However if you'd like to get to the same place I am try the following -
Firstly put an NSLog in didReceiveRemoteNotification, run your app on the device, put it into the background and send it a push notification. If you see your NSLog you'll know that the content-available flag is set correctly in your push.
Next add an NSLog in -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations. If this gets hint then you know that you are indeed enabling location services.
If you get this far you've probably got the same problem I have. 30 seconds isn't always long enough to get a location to the accuracy I want it.
BTW if you're using an iPhone 5 or newer you can use deferred location and keep location services running constantly. Sadly I need it to work on an iPhone 4s.
UPDATE -
I've now found that this is specifically related to background push notifications and didReceiveRemoteNotification. Enabling Location Manager from a standard background task will work as it did before iOS7. Therefore you can still use background tasks together with Location Manager to get your position every x minutes, you just can't start the whole thing off using a background push.
Answering to myself, and using severals stackoverflow's posts, it's apparently impossible to re-active the location update when you're in background. To keep being updates of your location updates, you have to let it run even if you're going in background !!
:)
I need to run specific code if a local notification was fired while the application was in the background and has now entered the foreground. One way to do this is to get the badge count, is there a better way?
Check out the docs at http://developer.apple.com/library/mac/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/IPhoneOSClientImp/IPhoneOSClientImp.html
Your App Delegate can detect notifications when the app is in the background
- (void)application:(UIApplication *)app didReceiveLocalNotification:(UILocalNotification *)notif {
NSString *itemName = [notif.userInfo objectForKey:ToDoItemKey]
[viewController displayItem:itemName]; // custom method
application.applicationIconBadgeNumber = notification.applicationIconBadgeNumber-1;
}