1

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.

Community
  • 1
  • 1
wufoo
  • 13,571
  • 12
  • 53
  • 78

3 Answers3

3

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.

Matt
  • 1,586
  • 8
  • 12
  • That sounds like what I'm looking for but after cloning your link and looking through the TableViewPull Demo project, I'm not sure I see where the HTTP connection is happening. It looks like it's supposed to happen in the reloadTableViewDataSource method, is that correct? – wufoo Sep 18 '13 at 19:05
  • The library gives a view class that you can put above the tableview, such that it becomes visible when the user pulls down on the table (bounces enabled), and goes out of view when the user releases and the table scrolls back to the first row. The tables scroll delegate can then forward the appropriate methods on the EGOView such that the labels and arrow can update. You then have some object become the delegate of the EGOView, which will trigger the data to reload from the server. The library is only for implementing pull to refresh UI and trigger, not the actual logic of HOW you refresh. – Matt Sep 18 '13 at 20:00
1

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.

Ben S
  • 68,394
  • 30
  • 171
  • 212
0

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);
}
wufoo
  • 13,571
  • 12
  • 53
  • 78