Normally the below code is working fine but when I execute this code from remote notification(APNs) "queue2" not get execute, I am curious to know what I am missing in below code snippet.
//On didReceiveRemoteNotification, I am calling refresh method using singleton object
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
[[NetworkDispatcher sharedInterface] refresh];
}
//Created two queues, initialise in "NetworkDispatcher" init method
dispatch_queue_t queue1 = dispatch_queue_create("com.Test.NetworkDispatcher", NULL);
dispatch_queue_t queue2 = dispatch_queue_create("com.Test.NetworkDispatcher.PostRequests", NULL);
//and below is the "refresh" method in "NetworkDispatcher" class
- (void)refresh
{
dispatch_async(queue1, ^{
if ([self refreshWorker])
{
dispatch_async(queue2, ^{
[self syncAllParties];
});
}
});
}
dispatch_async(queue2, gets hit but syncAllParties doesn't get called all the times
after some times when other post api has been called - this automatically starts getting executed
at that time all the request queued will get processed as if queue was locked and got unlocked by these POST operation
Any help would be appreciated.
Related
I want to execute a function before an NSOperation is cancelled. In main function, I add below code to achieve this goal:
if (self.isCancelled) {
[self doSomething];
return;
}
But if I cancel an operation before its start method is called, where should I call doSomething?
For queued operations, it simply marks the operation as ready to
execute and lets the queue call its start method, which subsequently
exits and results in the clearing of the operation from the queue.
According to above Apple's document, I know that I can call doSomething in start function, so am I right?
- (void)start {
if (self.isCancelled) {
[self doSomething];
}
[super start];
}
I would set the code you want to run in the completionblock.
I am creating an application where I am retrieving data from the server like below:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self retrievedatafromserver];
dispatch_async(dispatch_get_main_queue(), ^{
//UIUpdation, fetch the image/data from DB and update into your UI
});
});
How do I retrieve data from the server even if application goes to background?
Thanks & Regards
sumana
If Your scope of project is in only iOS 7 then you can use A new background mode which comes in the iOS 7 and onwards. You can fetch the data in background mode without any extra efforts of coding.
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
Now that your app already knows to initiate background fetch, let’s tell it what to do. The method -(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler will assist in doing so. This method is called every time that a background fetch is performed, and should be included in the AppDelegate.m file. The complete version is provided below:
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
UINavigationController *navigationController = (UINavigationController*)self.window.rootViewController;
id topViewController = navigationController.topViewController;
if ([topViewController isKindOfClass:[ViewController class]]) {
[(ViewController*)topViewController insertNewObjectForFetchWithCompletionHandler:completionHandler];
} else {
NSLog(#"Not the right class %#.", [topViewController class]);
completionHandler(UIBackgroundFetchResultFailed);
}
}
Now in your controller. Do like that
- (void)insertNewObjectForFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
NSLog(#"Update the tableview.");
self.numberOfnewPosts = [self getRandomNumberBetween:0 to:4];
NSLog(#"%d new fetched objects",self.numberOfnewPosts);
for(int i = 0; i < self.numberOfnewPosts; i++){
int addPost = [self getRandomNumberBetween:0 to:(int)([self.possibleTableData count]-1)];
[self insertObject:[self.possibleTableData objectAtIndex:addPost]];
}
/*
At the end of the fetch, invoke the completion handler.
*/
completionHandler(UIBackgroundFetchResultNewData);
}
Note :- If you have to give supportability on iOS 6 and below then avoid this approach. Because it's not available.
When your app enters background mode. you can access code for couple of seconds. Suppose the background queue is still performing and you entered background. then you might need to recall the method when app entered foreground. (take a bool variable and check whether the process is completed or not, if process is completed no issues. if not call the method again.).
If you want to make app run in background mode also then you need to request for background run mode in plist. See this link for reference only for these features we can active background run mode and you can active any of them according to you usage http://blogs.innovationm.com/support-for-applications-running-in-background-ios/
ios noob here: I have an ipad app as an In/Out board posted in the office. Basically, it runs one app; a TableView of 14 people and whether they are in or out of the office. At their desks, people hit a button on a web page to indicate their status when they leave for lunch, meeting or whatever. The ipad app then contacts our webserver every 5 minutes to retrieve an updated status list.
I've found a couple old postings on Stack, one here, which says all downloading must happen in the foreground of the application. The post is from 2011 so wondering if things have changed? I would rather not have the UI locked-up every 5 minutes if someone wants too look at the bottom of the list while a refresh is happening.
That post is about the app being in the background, your use case suggests someone is using the app, and it is in the foreground. You can of course do a web request on a background thread without locking the UI thread. The general pattern for your scenario is, when the view appears or the app becomes active, refresh the data (on a background thread), refresh the table (on the main thread), and then set your timer for an automatic refresh (and disable it when the app goes into the background), and potentially implement some kind of 'pull to refresh' feature (https://github.com/enormego/EGOTableViewPullRefresh).
If you do those things, your data will be up to date when people are viewing the app, and users can guarantee it by pulling to refresh.
Yes! Things have changed. It's now possible (as of iOS 7) to run HTTP requests while the app is backgrounded.
In order to do so, you need to add the value fetch to your app's UIBackgroundModes Info.plist key.
For more details see the iOS App Programming Guide.
After looking through a lot of code and a dizzying array of ways to do this, I really couldn't find a "simple" example. Many examples on the net are pre-ARC, or too complex for my level of understanding. Still other examples hinged on 3rd party libraries which are no longer in development. Still other examples, more up to date, have timeouts of 30 seconds in which everything must be completed (ios7 fetch) which doesn't seem like enough time for a quick download on a busy wi-fi network. Eventually, I did manage to piece together a working sample which does run a background download every 20 seconds. Not sure how to update the UI yet.
AppDelegate.m
#import "bgtask.h"
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
bgtask *b = [[bgtask alloc] initTaskWithURL:#"http://www.google.com" app:application];
return YES;
}
bgtask.h
#import <Foundation/Foundation.h>
#interface bgtask : NSOperation
#property (strong, atomic) NSMutableData *webData;
#property (strong, atomic) UIApplication *myApplication;
- (id) initTaskWithURL:(NSString *)url app:(UIApplication *)application;
#end
bgtask.m
#import "bgtask.h"
#implementation bgtask
UIBackgroundTaskIdentifier backgroundTask;
#synthesize webData = _webData;
#synthesize myApplication = _myApplication;
NSString *mURL;
// connect to webserver and send values. return response data
- (void) webConnect
{
NSURL *myURL = [NSURL URLWithString:mURL];
_webData = [NSData dataWithContentsOfURL:myURL];
if (_webData)
{
// save response data if connected ok
NSLog(#"connetion ok got %ul bytes", [_webData length]);
}
else
{
NSLog(#"connection failed");
//TODO: some error handling
}
}
- (void) timerTask:(NSTimer *) timer
{
backgroundTask = [_myApplication beginBackgroundTaskWithExpirationHandler:
^{
dispatch_async(dispatch_get_main_queue(),
^{
if (backgroundTask != UIBackgroundTaskInvalid)
{
[_myApplication endBackgroundTask:backgroundTask];
backgroundTask = UIBackgroundTaskInvalid;
}
});
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
NSLog (#"Running refresh...");
[self webConnect];
dispatch_async(dispatch_get_main_queue(),
^{
if (backgroundTask != UIBackgroundTaskInvalid)
{
[_myApplication endBackgroundTask:backgroundTask];
backgroundTask = UIBackgroundTaskInvalid;
}
});
});
}
- (id) initTaskWithURL:(NSString *)url app:(UIApplication *)application
{
self = [super init];
if (self)
{
// setup repeating refresh task.
// Save url, application for later use
mURL = [[NSString alloc] initWithString:url];
_myApplication = application;
[NSTimer scheduledTimerWithTimeInterval:20.0
target:self
selector:#selector(timerTask:)
userInfo:nil
repeats:YES];
NSLog (#"task init");
}// if self
return (self);
}
I am using a serial dispatch queue to serialize some network requests when the user moves the app to the background.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
dispatch_queue_t opQ = dispatch_queue_create("com.myapp.network", NULL);
dispatch_async(opQ, ^{
[self sendNetworkData1];
[self sendNetworkData2];
[self sendNetworkData3];
});
}
The problem is that when they run on this queue I have created, the app doesn't stay active even for the 5 seconds it is supposed to.
On the contrary, when I send the same requests outside of a queue, they are being sent for approximately 8 sec. but the app crashes afterwards.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[self sendNetworkData1];
[self sendNetworkData2];
[self sendNetworkData3];
}
I would also like to write the remaining ones on the disk, so that they can be sent the next time the user opens the app.
What's the best way to implement this?
When the application enters the background if it requires additional time to complete some task you will want to notify the OS of that. The detailed documentation is here: http://developer.apple.com/library/ios/#DOCUMENTATION/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html. Here's a quick and dirty patch.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
__block UIBackgroundTaskIdentifier backgroundTask; //Create a task object
backgroundTask = [application beginBackgroundTaskWithExpirationHandler: ^ {
[application endBackgroundTask:background_task];
backgroundTask = UIBackgroundTaskInvalid; //Set the task to be invalid
}];
dispatch_queue_t opQ = dispatch_queue_create("com.myapp.network", NULL);
dispatch_async(opQ, ^{
[self sendNetworkData1];
[self sendNetworkData2];
[self sendNetworkData3];
[application endBackgroundTask:background_task];
backgroundTask = UIBackgroundTaskInvalid; //Set the task to be invalid
});
}
The bottom line is that you notify that the application needs to run in the background with beginBackgroundTaskWithExpirationHandler: THen when your done you call endBackgroundTask: to notify the OS that you are finished processing in the background. And finally make sure that you reset the backgroundTask variable to UIBackgroundTaskInvalid.
I'm using GCD for background downloading in my Tab Bar app.
First step is to do some background downloading in -viewWillAppear: (to setup some basic data before the view is loaded).
Second step is to the rest of the background downloading in -viewDidAppear:
For some reason, the dispatch block in -viewDidAppear: gets called before the dispatch block in -viewWillAppear:.
This only happens once after loading the application for the first time switching to the tab with the GCD background methods. Switching to another tab and then switching back to the tab with the GCD background methods. The third (and all the rest subsequent times) time I'm switching back it's works as expected (-viewWillAppear: firing first and then -viewDidAppear:).
Here are excerpts of my code (-viewWillAppear: and -viewDidAppear:):
-viewWillAppear:
- (void)viewWillAppear:(BOOL)animated {
DLog(#"viewWillAppear method running");
[super viewWillAppear:animated];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[self setDiskCareerIds:[CareersParser idsFrom:#"disk"]];
[self setDownloadedCareerIds:[CareersParser idsFrom:#"web"]];
DLog(#"diskCareerIds after being set in viewWillAppear: %#", [self diskCareerIds])
DLog(#"downloadedCareerIds after being set in viewWillAppear: %#", [self downloadedCareerIds])
if ([[self downloadedCareerIds] isEqualToArray:[self diskCareerIds]]) {
DLog(#"viewWillAppear: ids equal, loading careers from disk.");
self.careers = [CareersParser loadCareersFromDisk];
dispatch_async(dispatch_get_main_queue(), ^{
[self.table reloadData];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
});
}
});
//[self downloadData];
}
-viewDidAppear:
- (void)viewDidAppear:(BOOL)animated {
DLog(#"viewDidAppear method running");
[super viewDidAppear:animated];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
if (![[self downloadedCareerIds] isEqualToArray:[self diskCareerIds]]) {
DLog(#"ids not equal, saving careers to disk.");
dispatch_async(dispatch_get_main_queue(), ^{
[self showLoadingView];
});
[CareersParser saveCareersToDisk];
self.careers = [CareersParser loadCareersFromDisk];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.table reloadData];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
[self removeLoadingView];
});
});
//[self download3];
//[self downloadData];
}
Check the debug log at Pastie.
Well, you're printing that log message in that first block (the one scheduled in viewWillAppear:) after it has done a bunch of parsing, not when it actually starts executing.
The thing is that global queue is a concurrent queue. So even though you are scheduling that first block first, it's not surprising that it sometimes falls behind the other block which is executing concurrently with it.
One simple answer would be to create a serial queue, and then you'll be sure the first block completes before the second one is executed. That seems to be what you want, right?