Goal is to notify end user about reachability changed when app is in background.
Broadcasting approach NSNotificationCenter not working in background.
second approach i tried beginBackgroundTaskWithExpirationHandler in applicationDidEnterBackground but associated issue with it:
a) Time limitation of 10 min
b) i calling a end loop for detecting the reachability state which run on main thread and in result it choke the UI. Code:-
-(void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(#"Application entered background state.");
// bgTask is a property of the class
if (isUserLogin) {
NSAssert(self.bgTask == UIBackgroundTaskInvalid, nil);
bgTask = [application beginBackgroundTaskWithExpirationHandler: ^{
dispatch_async(dispatch_get_current_queue(), ^{
[application endBackgroundTask:self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
});
}];
dispatch_async(dispatch_get_current_queue(), ^{
while ([application backgroundTimeRemaining] > 1.0) {
curReach = [[utilities sharedUtility]reachabilityChanged];
NetworkStatus netStatus = [curReach currentReachabilityStatus];
NSLog(#"-----------back ground task------------");
if (netStatus == ReachableViaWWAN || netStatus == ReachableViaWiFi) {
//isNetworkMessagePopup = YES;
}
else{
// if (isNetworkMessagePopup) {
// isNetworkMessagePopup = NO;
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif) {
localNotif.alertBody = FORMAT_UNABLE_TO_CONNECT_SERVER_DUE_TO_NO_INTERNET;
localNotif.alertAction = NSLocalizedString(#"Read Message", nil);
localNotif.soundName = #"pushNotification.caf";
localNotif.repeatInterval = 5;
//localNotif.applicationIconBadgeNumber = 1;
[application presentLocalNotificationNow:localNotif];
[localNotif release];
break;
//}
}
}
}
[application endBackgroundTask:self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
});
}
}
Please suggest me is it possible to notify user in iOS whenever reachability changed and app in background state?
If yes then how can i achieve this.
Thanks.
You could possibly use the background fetch capability as described in the iOS App Programming Guide to check reach ability and post a local notification if the network is un available.
There is no guarantee of how often this will be called - probably in the order of every few minutes or longer - certainly not every few seconds.
Related
Example:-
I have generated 4 local notifications with 4 types in minimize State.
Type of notifications below.
Message
Friend Request
Video call
Audio call
notifications are shown in notification center.
Noow..I have clicked on 2nd notification,
How can i get which number of notification clicked?
How can i get 2nd notification body alert text(content)?
this mehod is not working for minimize state
like whatsApp.. I need to go on specific screen according to Notification type.
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type {
NSString *Notitype=#"";
if ([[payload.dictionaryPayload valueForKey:#"type"]isEqualToString:#"video"]) {
Notitype=#"video";
}
else if ([[payload.dictionaryPayload valueForKey:#"type"]isEqualToString:#"friendRequest"]) {
Notitype=#"friend Request";
}
else if([[payload.dictionaryPayload valueForKey:#"type"] isEqualToString:#"message"] )
{
Notitype=#"message";
}
else{
Notitype=#"audio";
}
I'm creating UILocalNotification like this :
UILocalNotification *notification = [[UILocalNotification alloc]init];
notification.repeatInterval = NSDayCalendarUnit;
[notification setAlertBody:Notitype];
[notification setFireDate:[NSDate dateWithTimeIntervalSinceNow:1]];
[notification setTimeZone:[NSTimeZone defaultTimeZone]];
[[UIApplication sharedApplication] setScheduledLocalNotifications:[NSArray arrayWithObject:notification]];
For Set local notification as:
// Schedule the notification
UILocalNotification *notification = [[UILocalNotification alloc]init];
notification.repeatInterval = NSDayCalendarUnit;
[notification setAlertBody:Notitype];
[notification setFireDate:[NSDate dateWithTimeIntervalSinceNow:1]];
[notification setTimeZone:[NSTimeZone defaultTimeZone]];
//Create user info for local notification.
NSDictionary* dict = #{#"Type": #"message", #"ID": #"set your unique ID", #"body": notification.alertBody,};
notification.userInfo = dict;
//You can Set Type as: message, friend Request, video call, Audio call.
[[UIApplication sharedApplication] setScheduledLocalNotifications:[NSArray arrayWithObject:notification]];
for get which type of local notifications is click than you can use didReceiveLocalNotification delegate.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
//[super application:application didReceiveLocalNotification:notification]; // In most case, you don't need this line
//Get notification type
NSString *notificationType = [notification.userInfo valueForKey:#"Type"];
//notificationType as: message, friend Request, video call, Audio call.
NSString *notificationBody = [notification.userInfo valueForKey:#"Notification_body"];
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateActive) {
// Application in foreground when notification is delivered.
if ([notificationType isEqual:#"message"]) {
//Handle message
} else if ([notificationType isEqual:#"friend Request"]) {
//Handle friend Request
} else if ([notificationType isEqual:#"video call"]) {
//Handle video call
} else if ([notificationType isEqual:#"Audio call"]) {
//Handle Audio call
}
} else if (state == UIApplicationStateBackground) {
// Application was in the background when notification was delivered.
if ([notificationType isEqual:#"message"]) {
//Handle message
} else if ([notificationType isEqual:#"friend Request"]) {
//Handle friend Request
} else if ([notificationType isEqual:#"video call"]) {
//Handle video call
} else if ([notificationType isEqual:#"Audio call"]) {
//Handle Audio call
}
} else {
}
}
Update
Also check this same things in DidFinishLaunchingWithOptions, possible user don't react on notification, but tap on App icon to open app. So in case of video call or anything urgent then it must be handled through DidFinishLaunchingWithOptions
I tried to use NSOperation and NSOperationQueue.
But As getting into background, NSOperation didn't work. it stopped.
Calling
- (void)viewDidLoad
{
findFQueue = [[NSOperationQueue alloc] init];
FindFeatureOperation *testOperation = [[FindFeatureOperation alloc] init];
[findFQueue addOperation:testOperation];
}
NSOperation define
#import "FindFeatureOperation.h"
#implementation FindFeatureOperation
- (void)main
{
for (int i = 0; i < 999999 ; i++) {
NSLog(#"Operation %d", i);
[NSThread sleepForTimeInterval:1];
}
}
#end
Let me Know Please.
1 way: inside the op
to keep just running the operation:
- (main) {
bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
// Clean up any unfinished task business by marking where you
// stopped or ending the task outright.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
for (int i = 0; i < 999999 ; i++) {
NSLog(#"Operation %d", i);
[NSThread sleepForTimeInterval:1];
}
[application endBackgroundTask];
}
2 way: using inside of applicationDidEnterBG:
- (void)applicationDidEnterBackground:(UIApplication *)app {
_bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
// When Expired the limit time for background NSLog(#"Expired Background Task Limit Time");
[app endBackgroundTask:_bgTask];
_bgTask = UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[findFQueue waitUntilAllOperationsAreFinished];
[app endBackgroundTask];
});
}
Note: you can still be canceled if you use too much cpu / men / energy ... see the docs
Note 2: This can be written anywhere. I placed it in the OP but maybe in the VC is a better place... I don't think so but might be
Note 3: Read the docs! :: https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html
I want to fetch service for every 15 mins,so i am using NSTimer.It is working fine.But how to call same service while app is in background state by using nstimer.Nstimer is not working in background state.Please suggest me.
//when the application is about to move from active to inactive state.
- (void)applicationWillResignActive:(UIApplication *)application
{
[self sendBackgroundLocationToServer];
}
- (void) sendBackgroundLocationToServer
{
UIBackgroundTaskIdentifier bgTask = UIBackgroundTaskInvalid;
bgTask = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
}];
//Start Timer
[self startTimer];
//Close the task
if (bgTask != UIBackgroundTaskInvalid)
{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
}
}
I am trying to run some background tasks once the user quits the app.
- (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.
if ([[UIDevice currentDevice] respondsToSelector:#selector(isMultitaskingSupported)]) { //Check if our iOS version supports multitasking I.E iOS 4
if ([[UIDevice currentDevice] isMultitaskingSupported]) { //Check if device supports mulitasking
UIApplication *application = [UIApplication sharedApplication]; //Get the shared application instance
__block UIBackgroundTaskIdentifier background_task; //Create a task object
background_task = [application beginBackgroundTaskWithExpirationHandler: ^ {
[application endBackgroundTask: background_task]; //Tell the system that we are done with the tasks
background_task = UIBackgroundTaskInvalid; //Set the task to be invalid
//System will be shutting down the app at any point in time now
}];
//Background tasks require you to use asyncrous tasks
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Perform your tasks that your application requires
NSLOG(#"test1");
[self performSelector:#selector(checkActivityCount) withObject:nil afterDelay:3];
[application endBackgroundTask: background_task]; //End the task so the system knows that you are done with what you need to perform
background_task = UIBackgroundTaskInvalid; //Invalidate the background_task
});
}
}
}
-(void)checkActivityCount{
NSLog(#"test");
NSString *urlstring = #"https://exampleapp.com/api/v1/postactivity/?unreadfeedcount=yes";
NSURL *url = [NSURL URLWithString:urlstring];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
BOOL success = [[JSON objectForKey:#"success"] boolValue];
if (success) {
if (![JSON[#"unread_activity_count"] isEqualToString:#"0"]) {
// Schedule the notification
UILocalNotification* localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:2];
localNotification.alertBody = [NSString stringWithFormat:#"You have %# unread activities.",JSON[#"unread_activity_count"]];
localNotification.alertAction = #"Show me the item";
localNotification.timeZone = [NSTimeZone defaultTimeZone];
localNotification.applicationIconBadgeNumber = [[UIApplication sharedApplication] applicationIconBadgeNumber] + 1;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
// Request to reload table view data
[[NSNotificationCenter defaultCenter] postNotificationName:#"reloadData" object:self];
}
}
} failure:nil];
[operationQueue addOperation:operation];
[self performSelector:#selector(checkActivityCount) withObject:nil afterDelay:3000];
}
Now test1 gets logged, but test never logs. Am I doing something wrong in the code?
Using performSelector:withObject:afterDelay: pushes the request onto the queue for the thread. It doesn't wait for it. So immediately after you do that (even if something would pick the item from the queue in 3 seconds) you're telling the app that the background task is complete and the app gets shut down.
I haven't tried pausing like this, but try something more like:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self checkActivityCount];
[application endBackgroundTask: background_task];
background_task = UIBackgroundTaskInvalid;
});
I have been testing different way to implement the possibility to know if the device get internet back when the app it is in background so the first code I test was the Apple reachability sample code http://developer.apple.com/library/ios/#samplecode/Reachability/Introduction/Intro.html
But this code doesn´t notify internet state when the App it´s in background. So I tried also the folowing code and it work when App is launched from Background state to foreground (same as Apple reachability sample code)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// check for internet connection
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(checkNetworkStatus:)
name:kReachabilityChangedNotification object:nil];
// Set up Reachability
internetReachable = [[Reachability reachabilityForInternetConnection] retain];
[internetReachable startNotifier];
...
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// check for internet connection
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(checkNetworkStatus:)
name:kReachabilityChangedNotification object:nil];
// Set up Reachability
internetReachable = [[Reachability reachabilityForInternetConnection] retain];
[internetReachable startNotifier];
}
- (void)checkNetworkStatus:(NSNotification *)notice {
// called after network status changes
NetworkStatus internetStatus = [internetReachable currentReachabilityStatus];
switch (internetStatus)
{
case NotReachable:
{
NSLog(#"The internet is down.");
break;
}
case ReachableViaWiFi:
{
NSLog(#"The internet is working via WIFI");
//Alert sound in Background when App have internet again
UILocalNotification *notification = [[[UILocalNotification alloc] init] autorelease];
if (notification) {
[notification setFireDate:[NSDate date]];
[notification setTimeZone:[NSTimeZone defaultTimeZone]];
[notification setRepeatInterval:0];
[notification setSoundName:#"alarmsound.caf"];
[notification setAlertBody:#"Send notification internet back"];
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
break;
}
case ReachableViaWWAN:
{
NSLog(#"The internet is working via WWAN!");
//Alert sound in Background when App have internet again
UILocalNotification *notification = [[[UILocalNotification alloc] init] autorelease];
if (notification) {
[notification setFireDate:[NSDate date]];
[notification setTimeZone:[NSTimeZone defaultTimeZone]];
[notification setRepeatInterval:0];
[notification setSoundName:#"alarmsound.caf"];
[notification setAlertBody:#"Send notification internet back"];
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
break;
}
}
}
My question is : What is the way to get notified when internet state changed when the app is in Background ?
Live you cannot change if the network connection changed, the very best you can do to make it work is to use Background Fetch mode from Capabilities. Firstable you need to check the checkbox for background mode:
Then you need to ask for time interval as often as you can the sooner the better so i suggest application:didFinishLaunchingWithOptions: and you need to put this line:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
return YES;
}
The UIApplicationBackgroundFetchIntervalMinimum it's as often as possible, but it's not exact number of seconds between fetches from the docs:
The smallest fetch interval supported by the system.
And then when background fetch will fire you can check in AppDelegate with method:
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
Reachability *reachability = [Reachability reachabilityForInternetConnection];
[reachability startNotifier];
NetworkStatus status = [reachability currentReachabilityStatus];
switch (status) {
case NotReachable: {
NSLog(#"no internet connection");
break;
}
case ReachableViaWiFi: {
NSLog(#"wifi");
break;
}
case ReachableViaWWAN: {
NSLog(#"cellurar");
break;
}
}
completionHandler(YES);
}
All of this will work in iOS 7.0 or higher.
I don't believe there is a way to receive reachability notifications while you're in the background. The correct way to handle this would be to check for reachability in AppDelegate's - (void)applicationWillEnterForeground:(UIApplication *)application.
The only background event that background apps react to is receipt of push notifications, and that is because the OS wakes them up to do so, and then only when the user requests it.