I have an app that plays audio in the background. I'm trying to use beginBackgroundTaskWithExpirationHandler to skip to the next track when the current track is finished.
Here is the code that is called when the playback state changes. It never logs the "beginBG called" even though other code in the same method implements successfully in the background.
UIApplication *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[self ffwButtonPressed:ffwButton];
NSLog(#"beginBG called");
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
ffwButtonPressed invokes a few different methods to change the track. When that is complete...
UIApplication *app = [UIApplication sharedApplication];
if (bgTask != UIBackgroundTaskInvalid) {
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
NSLog(#"end bgTask");
Edit: Declaration
UIBackgroundTaskIdentifier bgTask;
Edit: In ViewDidLoad
bgTask = 0;
You are actually misunderstanding the function
-(UIBackgroundTaskIdentifier)beginBackgroundTaskWithExpirationHandler:(void (^)(void))handler
The block argument called "handler" is what will happen when the background task expire (10min).
To get your code running you need to put your code out of the expiration handler:
UIApplication *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
bgTask = UIBackgroundTaskInvalid;
};
[self ffwButtonPressed:ffwButton];
NSLog(#"beginBG called");
[app endBackgroundTask:bgTask];
Here is a link to the documentation
I ended up not needing beginBackgroundTaskWithExpirationHandler at all. This line solved my problem...
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
Does your App have a permission to run in the background as an Audio App? (need to put the right mode in your Info.plist)
See Apple's docs
beginBackgroundTaskWithExpirationHandler handler only get called when timer runout which is fairly large value. Real task as mentioned by Tomsum above should not be part part of block. Also remember to call endBackgroundTask: at end if backGroundTaskID != UIBackgroundTaskInvalid and only thing need to be done in block is setting back task variable = UIBackgroundTaskInvalid
Swift Version:
var backgroundTask: UIBackgroundTaskIdentifier? = 0
backgroundTask = UIApplication.shared.beginBackgroundTask(withName: "backgroundTask", expirationHandler: {() -> Void in
backgroundTask = UIBackgroundTaskInvalid
})
// Perform actions in background
YOUR_CODE_GOES_HERE (e.g. fetch requests, etc.)
UIApplication.shared.endBackgroundTask(backgroundTask!)
there can be a little modify, we should add endBackgroundTask in expiration handler:
IApplication *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
};
[self ffwButtonPressed:ffwButton];
NSLog(#"beginBG called");
[app endBackgroundTask:bgTask];
Is this all your code? Maybe the bgTask variable is not initialized, You need to declare bgTask before you assign. Try this -
UIBackgroundTaskIdentifier bgTask = nil;
Related
This question already has an answer here:
iOS Multi-Tasking Track GPS Location
(1 answer)
Closed 9 years ago.
I read some questions about app run in background and I know that there could b some possibilities to do so, like run an app monitoring the GPS data. I want to confirm is it possible to make a program like that non-stop at background and whenever GPS changes it start some other modules run for a while. After that this program still running in background ?
I hear some thing about : Adobe Air on mobile and run apps more than 10 mins. So can anyone confirm is it possible to do above requirement in normal iOS ? Thank you !
Use this code to run in background in app delegate class
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier bgTask = 0;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
To get gps data call gps location manager inside the above method
eg:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
locationmanager = [[CLLocationManager alloc]init];
[locationmanager setDelegate:self];
locationmanager.distanceFilter = kCLDistanceFilterNone;
[locationmanager setDesiredAccuracy:kCLLocationAccuracyBest];
return YES;
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[locationmanager startUpdatingLocation];
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier bgTask = 0;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation{
latitude = [[NSString alloc]initWithFormat:#"%g",newLocation.coordinate.latitude];
longitude = [[NSString alloc]initWithFormat:#"%g",newLocation.coordinate.longitude];
}
I need a long running task to be done in background as well as in foreground. This updates the core data. So to maintain UI responsive I created an another thread where I use different managedObjectContext(MOC). So a timer is set in background as well as in foreground and is inactivated appropriately when state changes. Before the task is starting and after the task is completed when I press home button it calls the two delegate methods properly but during the task is active when I press home button screen changes and UI hangs (becomes blank) but the two delegate methods are not called properly and the app is not terminated. I could not find the reason why this happens so. It would be helpful if someone can help.
I will attach the required code with this :
-(void) startTimerThread
{
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// Add code here to do background processing
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:[(AppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator]];
self.managedObjectContext = context;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:context];
NSLog(#"managedObjContext : %#\n",self.managedObjectContext);
[self getDataFromFile];
dispatch_async( dispatch_get_main_queue(), ^{
// Add code here to update the UI/send notifications based on the
// results of the background processing
[[NSNotificationCenter defaultCenter] postNotificationName:#"ReloadAppDelegateTable" object:nil];
[context release];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSManagedObjectContextDidSaveNotification
object:context];
});
});
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(#"Background\n");
[self.notificationTimer invalidate];
self.notificationTimer = nil;
UIApplication *app = [UIApplication sharedApplication];
self.bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
//start location update timer and background timer
self.timer = [NSTimer scheduledTimerWithTimeInterval:180 target:self
selector:#selector(startLocationServices) userInfo:nil repeats:YES];
self.locationManager.delegate = self;
[self.locationManager startUpdatingLocation];
self.logDownloader.managedObjectContext = self.managedObjectContext;
NSLog(#"managedObjContext : %#\n",self.logDownloader.managedObjectContext);
self.backgroundTimer = [NSTimer scheduledTimerWithTimeInterval:90 target:self.logDownloader selector:#selector(getDataFromFile) userInfo:nil repeats:YES];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
NSLog(#"Foreground\n");
//invalidate background timer and location update timer
[self.timer invalidate];
[self.backgroundTimer invalidate];
self.timer = nil;
self.notificationTimer = nil;
self.logDownloader.managedObjectContext = self.managedObjectContext;
NSLog(#"managedObjContext : %#\n",self.logDownloader.managedObjectContext);
[[NSNotificationCenter defaultCenter] postNotificationName:#"ReloadAppDelegateTable" object:nil];
self.notificationTimer = [NSTimer scheduledTimerWithTimeInterval:180 target:self.logDownloader selector:#selector(startTimerThread) userInfo:nil repeats:YES];
}
On iOS13+, if you implement UIWindowSceneDelegate, it calls func sceneDidEnterBackground(_ scene: UIScene), instead.
The reason why applicationDidEnterBackground: and applicationDidEnterForeground: are never called is because these methods are used in joint with Application does not run in background this option can be found in your ***-info.plist. If this option is set to YES than your app will never call these methods, because these when you press the home button with an app that has set the option to YES the instance of the app that is running will get terminated so everytime you press the home button and then select the app icon a new instance is being created so it is using applicationWillTerminate:.
The methods that Kirti mali has said would also be the incorrect methods to use for want you are after, the reason being is that applicationDidBecomeActive: and applicationWillResignActive: are used when something like when you answer a phone call. The instance running is not terminated neither is it sent to the background. The instance is paused until the user has finished on that call when it will become active again.
So the solution to this would be if you want the app to run in background would be to change the option "Application does not run in background" in the ***-info.plist to be NO just applicationDidBecomeActive: and applicationWillResignActive: is the wrong way for these methods to be used.
Please see the apple documentation on UIApplicationDelegate to get a better understanding of these methods.
Since your App runs on background those methods will never called however you can use willEnterForegroundNotification and didEnterBackgroundNotification to do same thing you want.
You can write following codes inside the didFinishLaunchingWithOptions method of ApplicationDelegate
NotificationCenter.default.addObserver(forName:UIApplication.willEnterForegroundNotification, object: nil, queue: nil) { (_) in
// Your Code here
}
NotificationCenter.default.addObserver(forName:UIApplication.didEnterBackgroundNotification, object: nil, queue: nil) { (_) in
// Your Code here
}
This method is called when home button is pressed
- (void)applicationDidBecomeActive:(UIApplication *)application
and this method is called when icon button is pressed
- (void)applicationWillResignActive:(UIApplication *)application
Is there a way to programmatically remove/dismiss UILocalNotification from Notification Tray.
I am able to cancel the notification which removes the notifications from
[[UIApplication sharedApplication] scheduledLocalNotifications]
Here is what i need to do
I need to dismiss the UILocalNotification from NotificationTray after the action has been performed(ie after the user taps the notification)
EDIT:
I can remove the notifications from the NSNotificationCenter. I want to remove specific notifications from the Notification Tray .Like the user presses the clear button to clear all the notifications belonging to a particular application.
You can cancel all notifications using:
[[UIApplication sharedApplication] cancelAllLocalNotifications];
If you want to remove a particular notification, you can use userinfo of notification object, when you create a local notification add a unique ID to that. Later you can use that ID for removing local notification.
For that you can use the following code:
NSString *notificationId = #"id_to_cancel";
UILocalNotification *notification = nil;
for(UILocalNotification *notify in [[UIApplication sharedApplication] scheduledLocalNotifications])
{
if([[notify.userInfo objectForKey:#"ID"] isEqualToString:notificationId])
{
notification = notify;
break;
}
}
[[UIApplication sharedApplication] cancelLocalNotification:notification];
I believe I had a similar issue. When the app entered the foreground I attempted to clear past notifications to remove any old notifications from the notifications tray.
I did something like this to grab old notifications and remove them:
NSArray *activeNotifications = [[UIApplication sharedApplication] scheduledLocalNotifications];
NSArray *pastNotifications = [activeNotifications filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"firDate < %#", [NSDate date]]];
for (UILocalNotification *notification in pastNotifications) {
[[UIApplication sharedApplication] cancelLocalNotification:notification];
}
However, it seems that scheduledLocalNotifications does not include locations whose fire date is already past even though they still appear in notification center.
Calling cancelAllLocalNotifications does seem to remove past notifications as well. So we can grab all the current notifications, cancel everything, and then add the ones we're still interested in back.
// Grab all the current notifications
NSArray *activeNotifications = [[UIApplication sharedApplication] scheduledLocalNotifications];
// Clear all notifications to remove old notifications
[[UIApplication sharedApplication] cancelAllLocalNotifications];
// Add back the still relevant notifications
for (UILocalNotification *notification in activeNotifications) {
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
Additionally we can do some filtering of the notifications before adding them back if some are no longer needed, and we can grab the active notifications when the app becomes active, store them in an instance variable, and only add them back when the app moves to the background
[UIApplication sharedApplication].applicationIconBadgeNumber = 0;
will do some trick too
but if you didnot use applicationIconBadgeNumber, it will not work, so trick is set
applicationIconBadgeNumber first :)
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:1];
If the Application is not running, you will be receiving the Local Notification object in the
-applicationDidFinishLaunchingWithOptions:
like:
UILocalNotification *localNotif = [launchOptions objectForKey: UIApplicationLaunchOptionsLocalNotificationKey];
or else you can get it in
(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
Now you can remove it from the Notification Center using
[[UIApplication sharedApplication]
cancelLocalNotification:notificationToCancel];
// deletes a pushnotification with userInfo[id] = id
-(void)deleteLocalPushNotificationWithId:(NSString*)id{
for(UILocalNotification *notification in [[UIApplication sharedApplication] scheduledLocalNotifications]){
if([[notification.userInfo objectForKey:#"id"] isEqualToString:id]){
[[UIApplication sharedApplication] cancelLocalNotification:notification];
}
}
}
// deletes all fired pushnotifications
-(void)clearLocalPushNotifications{
NSArray *activeNotifications = [[UIApplication sharedApplication] scheduledLocalNotifications];
// Clear all notifications to remove old notifications
[[UIApplication sharedApplication] cancelAllLocalNotifications];
// Add back the still relevant notifications
for (UILocalNotification *notification in activeNotifications) {
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
}
I was fiddling with some code and I was wondering why local notifications are stored in the notification center if the application is in the foreground. It's probably because Apple doesn't know what you are doing with them and honestly doesn't care; so they do their job.
As far as the question is concerned, I do the following:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
if (application.applicationState == UIApplicationStateActive)
{
NSLog(#"active");
// display some foreground notification;
[application cancelLocalNotification:notification];
}
else
{
NSLog(#"inactive");
}
}
So I just read this thread about how to close/remove all the already fired local notifications from the Notification center, if the user opens the app by clicking the app icon, not the notification. But after all of this, the other scheduled local notification should fire in the future.
Here is my easy solution for this, which should be triggered on application did becomeActive:
UIApplication* application = [UIApplication sharedApplication];
NSArray* scheduledNotifications = [NSArray arrayWithArray:application.scheduledLocalNotifications];
application.scheduledLocalNotifications = scheduledNotifications;
I ve tried the [[UIApplication sharedApplication] cancelLocalNotification:notification]; but it did not clear the already fired local notifications from the Notification center (outside of the app).
In my iPhone Timer Application,
In which a timer should run in background.
So,
I have set the notification in appdelegate it works perfectly...
With that I am calling the methods from view controller which makes timer alive.
Take a look some code...
App delegate
- (void)applicationDidEnterBackground:(UIApplication *)application
{
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
*/
NSLog(#"Time Remaining %d",(self.viewController.totalSeconds-self.viewController.totalCount));
[self.viewController selectandnotify:(self.viewController.totalSeconds-self.viewController.totalCount)];
[self.viewController stopTimer];
[self.viewController startTimerAction];
}
Here I am calling the method startTimerAction method which is in my view controller...take a look at this...
-(void)startTimerAction
{
timer_main = [NSTimer scheduledTimerWithTimeInterval:(1.0) target:self selector:#selector(ShowActicity) userInfo:nil repeats:YES];
}
Which is NSTimer
Here every time
-ShowActivity method will call after each second...Which is below in my view controller...
-(void)ShowActicity
{
NSLog(#"Total Counts %d",totalCount);
if (totalCount == totalSeconds) {
if ([timer_main isValid]) {
[timer_main invalidate];
isTimeOver = YES;
[self generateLog];
}
} else {
totalCount++;
seconds =seconds + 1;
if(seconds > 59)
{
minutes = minutes + 1;
seconds= 0;
}
}
How to call each time This method from view controller.....
How can I call each time showActivity method from appdelegate...
Should I use delegate for that
Should I create showActivity and timer in my Appdelegate..
Actually I want this application to run when view switches in app.....
I think If I make delegate is a good option?
Any other way....please have some suggestions
Generally use this code for background running .In the Background timer doesn't work
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIApplication* app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task.
[self startTimerAction];
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
http://developer.apple.com/library/ios/#DOCUMENTATION/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html#//apple_ref/doc/uid/TP40007072-CH4-SW3
I would like to remove old notifications that my app has made from the iOS 5 Notification Center. Can I do this? If so, how?
To remove notifications from the Notification Center simply set your icon badge number to zero.
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
This only works if the number changes, so if your app doesn't use the badge number you have to first set, then reset it.
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:1];
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
A more straightforward method that I use (and doesn't require badges) is to reset the array of scheduled local notifications to itself, as follows:
UIApplication* application = [UIApplication sharedApplication];
NSArray* scheduledNotifications = [NSArray arrayWithArray:application.scheduledLocalNotifications];
application.scheduledLocalNotifications = scheduledNotifications;
This has the effect that any scheduled notifications remain valid, while all "old" notifications that are present in Notification Center are removed. However, it also has the feel of something that might change in a future release of iOS, as I haven't seen any documentation for this behavior.
Of course, if you want to remove all notifications, it's simply the following:
[[UIApplication sharedApplication] cancelAllLocalNotifications];
Yes, you can cancel specific or all local notifications by calling
[[UIApplication sharedApplication] cancelLocalNotification:...];
or
[[UIApplication sharedApplication] cancelAllLocalNotifications];
If you want to clear notifications in swift and iOS 10.0
import UserNotifications
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.removeAllPendingNotificationRequests() // To remove all pending notifications which are not delivered yet but scheduled.
center.removeAllDeliveredNotifications() // To remove all delivered notifications
}
For me it only worked with sending a local notification with only a badge like this:
if([UIApplication sharedApplication].applicationIconBadgeNumber == 0) {
UILocalNotification *singleLocalPush = [[UILocalNotification alloc] init];
singleLocalPush.fireDate = [NSDate dateWithTimeIntervalSinceNow:1];
singleLocalPush.hasAction = NO;
singleLocalPush.applicationIconBadgeNumber = 1;
[[UIApplication sharedApplication] scheduleLocalNotification:singleLocalPush];
[singleLocalPush release];
} else {
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
}
And in the method
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
I can set the badge to 0 again.