WKWatchConnectivityRefreshBackgroundTask competing with WCSessionDelegate - ios

I'm trying to adapt my code from using only WCSessionDelegate callbacks in the foreground to accepting WKWatchConnectivityRefreshBackgroundTask via handleBackgroundTasks: in the background. The documentation states that background tasks may come in asynchronously and that one should not call setTaskCompleted until the WCSession's hasContentPending is NO.
If I put my watch app into the background and transferUserInfo: from an iPhone app, I am able to successfully receive my first WKWatchConnectivityRefreshBackgroundTask. However, hasContentPending is always YES, so I save away the task and simply return from my WCSessionDelegate method. If I transferUserInfo: again, hasContentPending is NO, but there is no WKWatchConnectivityRefreshBackgroundTask associated with this message. That is, subsequent transferUserInfo: do not trigger a call to handleBackgroundTask: –- they're simply handled by the WCSessionDelegate. Even if I immediately setTaskCompleted without checking hasContentPending, subsequent transferUserInfo: are handled by session:didReceiveUserInfo: without me needing to activate a WCSession again.
I'm not sure what to do here. There doesn't seem to be a way to force a WCSession to deactivate, and following the documentation about delaying setTaskCompleted seems to be getting me into trouble with the OS.
I've posted and documented a sample project illustrating my workflow on GitHub, pasting my WKExtensionDelegate code below. Am I making an incorrect choice or interpreting the documentation incorrectly somewhere along the line?
I've looked at the QuickSwitch 2.0 source code (after fixing the Swift 3 bugs, rdar://28503030), and their method simply doesn't seem to work (there's another SO thread about this). I've tried using KVO for WCSession's hasContentPending and activationState, but there's still never any WKWatchConnectivityRefreshBackgroundTask to complete, which makes sense to be given my current explanation of the issue.
#import "ExtensionDelegate.h"
#interface ExtensionDelegate()
#property (nonatomic, strong) WCSession *session;
#property (nonatomic, strong) NSMutableArray<WKWatchConnectivityRefreshBackgroundTask *> *watchConnectivityTasks;
#end
#implementation ExtensionDelegate
#pragma mark - Actions
- (void)handleBackgroundTasks:(NSSet<WKRefreshBackgroundTask *> *)backgroundTasks
{
NSLog(#"Watch app woke up for background task");
for (WKRefreshBackgroundTask *task in backgroundTasks) {
if ([task isKindOfClass:[WKWatchConnectivityRefreshBackgroundTask class]]) {
[self handleBackgroundWatchConnectivityTask:(WKWatchConnectivityRefreshBackgroundTask *)task];
} else {
NSLog(#"Handling an unsupported type of background task");
[task setTaskCompleted];
}
}
}
- (void)handleBackgroundWatchConnectivityTask:(WKWatchConnectivityRefreshBackgroundTask *)task
{
NSLog(#"Handling WatchConnectivity background task");
if (self.watchConnectivityTasks == nil)
self.watchConnectivityTasks = [NSMutableArray new];
[self.watchConnectivityTasks addObject:task];
if (self.session.activationState != WCSessionActivationStateActivated)
[self.session activateSession];
}
#pragma mark - Properties
- (WCSession *)session
{
NSAssert([WCSession isSupported], #"WatchConnectivity is not supported");
if (_session != nil)
return (_session);
_session = [WCSession defaultSession];
_session.delegate = self;
return (_session);
}
#pragma mark - WCSessionDelegate
- (void)session:(WCSession *)session activationDidCompleteWithState:(WCSessionActivationState)activationState error:(NSError *)error
{
switch(activationState) {
case WCSessionActivationStateActivated:
NSLog(#"WatchConnectivity session activation changed to \"activated\"");
break;
case WCSessionActivationStateInactive:
NSLog(#"WatchConnectivity session activation changed to \"inactive\"");
break;
case WCSessionActivationStateNotActivated:
NSLog(#"WatchConnectivity session activation changed to \"NOT activated\"");
break;
}
}
- (void)sessionWatchStateDidChange:(WCSession *)session
{
switch(session.activationState) {
case WCSessionActivationStateActivated:
NSLog(#"WatchConnectivity session activation changed to \"activated\"");
break;
case WCSessionActivationStateInactive:
NSLog(#"WatchConnectivity session activation changed to \"inactive\"");
break;
case WCSessionActivationStateNotActivated:
NSLog(#"WatchConnectivity session activation changed to \"NOT activated\"");
break;
}
}
- (void)session:(WCSession *)session didReceiveUserInfo:(NSDictionary<NSString *, id> *)userInfo
{
/*
* NOTE:
* Even if this method only sets the task to be completed, the default
* WatchConnectivity session delegate still picks up the message
* without another call to handleBackgroundTasks:
*/
NSLog(#"Received message with counter value = %#", userInfo[#"counter"]);
if (session.hasContentPending) {
NSLog(#"Task not completed. More content pending...");
} else {
NSLog(#"No pending content. Marking all tasks (%ld tasks) as complete.", (unsigned long)self.watchConnectivityTasks.count);
for (WKWatchConnectivityRefreshBackgroundTask *task in self.watchConnectivityTasks)
[task setTaskCompleted];
[self.watchConnectivityTasks removeAllObjects];
}
}
#end

From your description and my understanding it sounds like this is working correctly.
The way this was explained to me is that the new handleBackgroundTasks: on watchOS is intended to be a way for:
the system to communicate to the WatchKit extension why it is being launched/resumed in the background, and
a way for the WatchKit extension to let the system know when it has completed the work it wants to do and can therefore be terminated/suspended again.
This means that whenever an incoming WatchConnectivity payload is received on the Watch and your WatchKit extension is terminated or suspended you should expect one handleBackgroundTasks: callback letting you know why you are running in the background. This means you could receive 1 WKWatchConnectivityRefreshBackgroundTask but several WatchConnectivity callbacks (files, userInfos, applicationContext). The hasContentPending lets you know when your WCSession has delivered all of the initial, pending content (files, userInfos, applicationContext). At that point, you should call the setTaskCompleted on the WKWatchConnectivityRefreshBackgroundTask object.
Then you can expect that your WatchKit extension will soon thereafter be suspended or terminated unless you have received other handleBackgroundTasks: callbacks and therefore have other WK background task objects to complete.
I have found that when attaching to the processes with a debugger that OSs might not suspended them like they normally would, so it'd suggest inspecting the behavior here using logging if you want to be sure to avoid any of those kind of issues.

Related

Keeping my WatchKit complications up to date when not running

I'm working on the WatchKit Extension of my app, and have some issues with complications.
I have a complication that displays a given total amount, which depends of what the user is doing on the iOS app. When the WatchKit Extension is running, the iOS app updates the watch app context using the -[WCSession updateApplicationContext:] method. It works fine, and then in the ExtensionDelegate of my Watch app, I manually update the complication with the new data.
But this is OK only when the extension is running (if it's not, it won't get the application context until the next launch).
So I edited my code to send the complication data directly to the Watch when user changed something in the iOS app, using the -[WCSession transferCurrentComplicationUserInfo:] method (it's written in the documentation that the ExtensionDelegate should be woken up to receive the user info in background).
I've implemented the -session:didReceiveUserInfo: method of the ExtensionDelegate to update the complication when it received data from the iOS app, but it doesn't work when the extension is not running... (and I don't know if it ever receives the user info as I can't log it)
How should I do to keep my complications up to date even when the extension is not running??
Thanks
PS: I'm using the Watch Simulator, and to "close" the extension I just Reboot the Watch (from the Hardware menu)
Edit: I managed to log out statements when the app is not running (by opening the Watch Simulator system log), and I get these lines when the iOS send a new complication user data to the watch extension:
Oct 18 18:08:11 pc16 WatchApp Extension[26615]: Extension received
request to wake up for complication support.
Oct 18 18:08:11 pc16 assertiond[26585]: assertion failed: 15A284 13S343: assertiond + 15398 [B48FCADB-A071-3A46-878B-538DC0AFF60B]: 0x1
So the watch receives well the user info dictionary, but seems to fail waking up the extension...
Edit 2: here is the part of code in the ExtensionDelegate that should receive the complication user info (but which is not called when the app is not running):
- (void) session: (WCSession *)session didReceiveUserInfo: (NSDictionary *)userInfo
{
NSLog(#"session:didReceiveUserInfo: %#", userInfo);
NSString *userInfoType = userInfo[KHWASessionTransferUserInfoType];
NSDictionary *userInfoContents = userInfo[KHWASessionTransferUserInfoContents];
// Complication Data
if ([userInfoType isEqualToString:KHWASessionTransferUserInfoTypeComplicationData]) {
// Store the complication data into user defaults
[[NSUserDefaults standardUserDefaults] setValue:userInfoContents[KHWAComplicationTotalBalance] forKey:KHWAComplicationTotalBalance];
[[NSUserDefaults standardUserDefaults] synchronize];
// And refresh the complications
CLKComplicationServer *complicationServer = [CLKComplicationServer sharedInstance];
for (CLKComplication *complication in complicationServer.activeComplications) {
[complicationServer reloadTimelineForComplication:complication];
}
}
}
Edit 3: the WCSession is set in the extension delegate applicationDidFinishLaunching method:
- (void) applicationDidFinishLaunching
{
// Setup the WatchConnectivity session
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
[...]
}
Wow, I finally resolved the issue!
It seems that, even if I didn't see it in the log files (see my last comment), the init method of WCExtensionDelegate is well called when waking up the app.
So I just had to move the WCSession setting bloc into the init method :
- (id) init
{
if (self = [super init]) {
// Setup the WatchConnectivity session
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
}
return self;
}
And for the while it works fine...

Send local notification when download completes through NSURLSession / NSURLSessionDownloadTask

I am using NSURLSessionDownloadTask objects on an NSURLSession to allow users to download documents while the app is in the background / device locked. I also want to inform the user that individual downloads have finished through a local notification.
To that end, I am triggering a local notification in the -URLSession:downloadTask:didFinishDownloadingToURL: download task delegate method, however I am wondering if there might be a better place to add the code triggering a notification, since the way Apple explains it, the download task will be passed to the system, and from that I am deriving that those delegates will not be called anymore on the download task's delegate once (or shortly after) the app is backgrounded.
My question: What is the best place to add the code for triggering the local notifications? Has anybody had any previous experience in adding this sort of a functionality to their application?
Answer on your question can be found in Apple documentation URL Loading System Programming Guide:
In iOS, when a background transfer completes or requires credentials,
if your app is no longer running, iOS automatically relaunches your
app in the background and calls the
application:handleEventsForBackgroundURLSession:completionHandler:
method on your app’s UIApplicationDelegate object. This call provides
the identifier of the session that caused your app to be launched.
Your app should store that completion handler, create a background
configuration object with the same identifier, and create a session
with that configuration object. The new session is automatically
reassociated with ongoing background activity. Later, when the session
finishes the last background download task, it sends the session
delegate a URLSessionDidFinishEventsForBackgroundURLSession: message.
Your session delegate should then call the stored completion handler.
If any task completed while your app was suspended, the delegate’s
URLSession:downloadTask:didFinishDownloadingToURL: method is then
called with the task and the URL for the newly downloaded file
associated with it.
As you see it's much more complicated then just set delegate object. By delegate methods you will be notified only if app in foreground mode. In other cases (app in background mode, app is terminated) you need handle AppDelegate methods that are described in above quote.
Also Apple provides example project, that shows how to work with background download/upload tasks. This example will help you to find place where to put "Local Notification" code.
As Visput explained above, this method will be called once the download completes.application:handleEventsForBackgroundURLSession:completionHandler:
This will happen if you use the NSURLSessionConfiguration class with the backgroundSessionConfiguraton. You might be missing that piece.
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:#"com.BGTransfer"];
sessionConfiguration.HTTPMaximumConnectionsPerHost = 5; // To set the max concurrent connections
It is explained in detail here.
As Suggested by #Gautam Jain u have to use backgroundSessionConfiguration to achieve ur objective.Below i have attached a example ,hope it helps you
DownloadModel.h
#import "AppDelegate.h"
#interface DownloadModel : NSObject<NSURLSessionDelegate,NSURLSessionTaskDelegate,NSURLSessionDownloadDelegate>{
NSString *resp;
}
+(instancetype)shared;
-(NSURLSessionDownloadTask *) downloadTaskWithURL:(NSURL*)url ;
#end
DownloadModel.m
#import "DownloadModel.h"
#interface DownloadModel ()
#property (strong,nonatomic) NSURLSession *downloadSession;
#end
#implementation DownloadModel
+(instancetype)shared{
static dispatch_once_t onceToken;
static DownloadModel *downloader=nil;
dispatch_once(&onceToken, ^{
downloader=[DownloadModel new];
});
return downloader;
}
-(id)init{
self=[super init];
if(self){
NSURLSessionConfiguration *downloadConfig=[NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"DownloadDemo"];
// downloadConfig.timeoutIntervalForRequest = 30;
// downloadConfig.timeoutIntervalForResource = 30;
// downloadConfig.HTTPMaximumConnectionsPerHost = 1;
// downloadConfig.sessionSendsLaunchEvents=YES;
downloadConfig.allowsCellularAccess = YES;
downloadConfig.networkServiceType = NSURLNetworkServiceTypeBackground;
// downloadConfig.discretionary = YES;
self.downloadSession=[NSURLSession sessionWithConfiguration:downloadConfig delegate:self delegateQueue:nil];
self.downloadSession.sessionDescription=#"Video Downloader";
}
return self;
}
-(NSURLSessionDownloadTask *) downloadTaskWithURL:(NSURL*)url{
return [self.downloadSession downloadTaskWithURL:url];
}
#pragma mark download delegate
use notification OR Local Notification in this method
- (void)URLSession:(NSURLSession *)session downloadTask(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL(NSURL *)location{
[[NSNotificationCenter defaultCenter] postNotificationName:#"DownloadFinish" object:downloadTask userInfo:nil];
}
For Progress of Download
- (void)URLSession:(NSURLSession *)session downloadTask(NSURLSessionDownloadTask *)downloadTask didWriteData(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
CGFloat progress=(CGFloat)totalBytesWritten/totalBytesExpectedToWrite;
NSDictionary *userInfo=#{#"progress":#(progress)};
[[NSNotificationCenter defaultCenter] postNotificationName:#"DownloadProgress" object:downloadTask userInfo:userInfo];
}
#pragma mark delegate
-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session{
AppDelegate *appdelegate=[[UIApplication sharedApplication] delegate];
if(appdelegate.backgroundSessionCompletionHandler){
appdelegate.backgroundSessionCompletionHandler();
appdelegate.backgroundSessionCompletionHandler=nil;
}
}
#end
AppDelegate.h
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (copy ,nonatomic) void(^backgroundSessionCompletionHandler)();
#end
AppDelegate.m
-(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler{
self.backgroundSessionCompletionHandler=completionHandler;
[DownloadModel shared];
}
ViewController.m Call this Method -(NSURLSessionDownloadTask *) downloadTaskWithURL:(NSURL*)url
- (void)viewDidLoad {
//Add Notification observers to track download progress and call the above method
[DownloadModel shared] downloadTaskWithURL:url];
}
Don't Forget to enable Background Fetch

EKEventStore Change Notifications Not Firing when app in Background

I have registered for Calendar Change Notifications using the following:
- (void) registerForLocalCalendarChanges
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(localCalendarStoreChanged) name:EKEventStoreChangedNotification object:self.store ];
}
This should call the following when a change is made to the local calendar:
- (void) localCalendarStoreChanged
{
// This gets called when an event in store changes
// you have to go through the calendar to look for changes
// launch this in a thread of its own!
ashsysCalendarEventReporter *eventReport = [ashsysCalendarEventReporter new];
NSLog(#"Local Calendar Store Changed");
[NSThread detachNewThreadSelector:#selector(getCalendarEvents) toTarget:eventReport withObject:nil];
}
BUT...when I start the app, then send it to the background so I can change a calendar entry, nothing happens when I change the calendar entry. It DOES fire when I return to the app. But, of course that is not the objective.
store is defined in the header file with:
#property (strong,nonatomic) EKEventStore *store;
Update...forgot to show the stuff I have in the background fetch.
This is in didFinishLaunchingWithOptions
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
This is in the app delegate:
- (void) application:(UIApplication*) application performFetchWithCompletionHandler:(void (^) (UIBackgroundFetchResult))completionHandler
{
// UIBackgroundTaskIdentifier uploadCalInfo = [application beginBackgroundTaskWithExpirationHandler:nil];
NSLog(#"A fetch got called");
// ashsysCalendarEventReporter *eventReport = [ashsysCalendarEventReporter new];
// [eventReport getCalendarEvents];
// // [NSThread detachNewThreadSelector:#selector(getCalendarEvents) toTarget:eventReport withObject:nil];
// [application endBackgroundTask:uploadCalInfo];
completionHandler(UIBackgroundFetchResultNewData);
The performFetch gets called at what seem like random times some not at all related to the calendar. Is there a way to find out what is firing the background fetch? Is it always the calendar? The actual execution is commented out -- is it correct?
What am I missing?
I've been trying to find this answer out myself and I don't think there's an actual way to accomplish this. As per Apple's documentation, we're not allowed access to system resources while in a background state:
Stop using shared system resources before being suspended. Apps that interact with shared system resources such as the Address Book or calendar databases should stop using those resources before being suspended. Priority for such resources always goes to the foreground app. When your app is suspended, if it is found to be using a shared resource, the app is killed.
I'm still looking for a better answer/work around but would love to hear this from anyone else.

When retrieving data from the server in dispatch queue, how to handle application going to background?

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/

How to run a background HTTP process in 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);
}

Resources