I am trying to get iOS background app fetch to work in my app. While testing in Xcode it works, when running on the device it doesn't!
我想让iOS后台应用程序获取在我的应用程序中工作。在Xcode中进行测试时,它可以正常运行,但在设备上运行却没有!
In application:didFinishLaunchingWithOptions I have tried various intervals with setMinimumBackgroundFetchInterval, including UIApplicationBackgroundFetchIntervalMinimum
在应用程序中:didFinishLaunchingWithOptions我用setMinimumBackgroundFetchInterval尝试了各种间隔,包括UIApplicationBackgroundFetchIntervalMinimum
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// tell the system we want background fetch
//[application setMinimumBackgroundFetchInterval:3600]; // 60 minutes
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
//[application setMinimumBackgroundFetchInterval:1800]; // 30 minutes
return YES;
}
I have implemented application:performFetchWithCompletionHandler
我已经实现了application:performFetchWithCompletionHandler
void (^fetchCompletionHandler)(UIBackgroundFetchResult);
NSDate *fetchStart;
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
fetchCompletiOnHandler= completionHandler;
fetchStart = [NSDate date];
[[NSUserDefaults standardUserDefaults] setObject:fetchStart forKey:kLastCheckedContentDate];
[[NSUserDefaults standardUserDefaults] synchronize];
[FeedParser parseFeedAtUrl:url withDelegate:self];
}
-(void)onParserFinished
{
DDLogVerbose(@"AppDelegate/onParserFinished");
UIBackgroundFetchResult result = UIBackgroundFetchResultNoData;
NSDate *fetchEnd = [NSDate date];
NSTimeInterval timeElapsed = [fetchEnd timeIntervalSinceDate:fetchStart];
DDLogVerbose(@"Background Fetch Duration: %f seconds", timeElapsed);
if ([self.mostRecentContentDate compare:item.date] <0) {
DDLogVerbose(@"got new content: %@", item.date);
self.mostRecentCOntentDate= item.date;
[self scheduleNotificationWithItem:item];
result = UIBackgroundFetchResultNewData;
}
else {
DDLogVerbose(@"no new content.");
UILocalNotification* localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:60];
localNotification.alertBody = [NSString stringWithFormat:@"Checked for new posts in %f seconds", timeElapsed];
localNotification.timeZOne= [NSTimeZone defaultTimeZone];
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
fetchCompletionHandler(result);
}
I have (successfully!) tested with the simulator and device using Xcode's Debug/SimulateBackgroundFetch
我已经(使用Xcode的Debug / SimulateBackgroundFetch)对模拟器和设备进行了测试(成功!)
I have successfully tested with a new scheme as shown in another SO answer (https://stackoverflow.com/a/29923802/519030)
我已经成功测试了一个新方案,如另一个SO答案所示(https://stackoverflow.com/a/29923802/519030)
When running on the device and not connected to Xcode, my code is not executing. I've opened the app, closed the app (not killed the app!), waited hours and days. I have tried logging in the fetch handers, and also written code to send local notifications.
在设备上运行但未连接到Xcode时,我的代码没有执行。我打开了应用程序,关闭了应用程序(没有杀死应用程序!),等待了几个小时和几天。我已经尝试登录fetch handers,还编写了代码来发送本地通知。
I once successfully saw my local notifications test on the device, and in fact iOS seemed to trigger the fetch three times, each about about fifteen minutes apart, but then it never occurred again.
我曾经成功地在设备上看到了我的本地通知测试,事实上iOS似乎触发了三次获取,每次大约相隔大约十五分钟,但之后它再也没有发生过。
I know the algorithm used to determine how frequently to allow the background fetch to occur is a mystery, but I would expect it to run at least occasionally within a span of days.
我知道用于确定允许背景提取发生频率的算法是一个谜,但我希望它能在一段时间内偶尔运行。
I am at a loss for what else to test, or how to troubleshoot why it seems to work in the simulator but not on the device.
我无法测试其他什么,或者如何解决为什么它似乎在模拟器中工作但不在设备上工作。
Appreciate any advice!
感谢任何建议!
20
Your problem is that you are returning from performFetchWithCompletionHandler
before you call the completion handler, since the network fetch operation is occurring the in the background and you call the completion handler in your delegate method. Since iOS thinks you aren't playing by the rules it will deny your ability to use background fetch.
您的问题是您在调用完成处理程序之前从performFetchWithCompletionHandler返回,因为网络提取操作正在后台进行,您在委托方法中调用完成处理程序。由于iOS认为您不遵守规则,因此它将拒绝您使用后台获取的能力。
To fix the problem you need to call beginBackgroundTaskWithExpirationHandler
and then end that task after you have called the completion handler.
要解决此问题,您需要调用beginBackgroundTaskWithExpirationHandler,然后在调用完成处理程序后结束该任务。
Something like:
就像是:
UIBackgroundTaskIdentifier backgroundTask
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
fetchCompletiOnHandler= completionHandler;
fetchStart = [NSDate date];
self.backgroundTask = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask:self.backgroundUpdateTask];
self.backgroundTask = UIBackgroundTaskInvalid;
}];
[[NSUserDefaults standardUserDefaults] setObject:fetchStart forKey:kLastCheckedContentDate];
[FeedParser parseFeedAtUrl:url withDelegate:self];
}
-(void)onParserFinished
{
DDLogVerbose(@"AppDelegate/onParserFinished");
UIBackgroundFetchResult result = UIBackgroundFetchResultNoData;
NSDate *fetchEnd = [NSDate date];
NSTimeInterval timeElapsed = [fetchEnd timeIntervalSinceDate:fetchStart];
DDLogVerbose(@"Background Fetch Duration: %f seconds", timeElapsed);
if ([self.mostRecentContentDate compare:item.date] <0) {
DDLogVerbose(@"got new content: %@", item.date);
self.mostRecentCOntentDate= item.date;
[self scheduleNotificationWithItem:item];
result = UIBackgroundFetchResultNewData;
}
else {
DDLogVerbose(@"no new content.");
UILocalNotification* localNotification = [[UILocalNotification alloc] init];
localNotification.alertBody = [NSString stringWithFormat:@"Checked for new posts in %f seconds", timeElapsed];
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
fetchCompletionHandler(result);
[[UIApplication sharedApplication] application endBackgroundTask:self.backgroundUpdateTask];
self.backgroundTask = UIBackgroundTaskInvalid;
}
My test app using this approach has been executing a fetch every 15 minutes initially, but it becomes less frequent over time. Without the background task it exhibited the same issue you are seeing.
我使用这种方法的测试应用程序最初每15分钟执行一次获取,但随着时间的推移它会变得不那么频繁。如果没有后台任务,它会显示您遇到的相同问题。
I found that setting the background fetch interval to something other than UIApplicationBackgroundFetchIntervalMinimum
also helps. My test app is running with a background fetch interval of 3600 (one hour) and has been reliably triggering for several days now; even after a phone restart and not running the app again. The actual trigger interval is 2-3 hours however.
我发现将背景提取间隔设置为UIApplicationBackgroundFetchIntervalMinimum以外的其他内容也有帮助。我的测试应用程序正在以3600(一小时)的后台获取间隔运行,并且已经可靠地触发了几天;即使手机重启后又没有再运行应用程序。但实际的触发间隔是2-3小时。
My sample app is here
我的示例应用就在这里
10
In order to implement background fetch there are three things you must do:
为了实现后台提取,您必须做三件事:
Background Fetch Frequency
背景提取频率
How frequent our application can perform Background Fetch is determined by the system and it depends on:
我们的应用程序执行背景提取的频率由系统决定,它取决于:
In other words, your application is entirely at the mercy of the system to schedule background fetch for you. In addition, each time your application uses background fetch, it has at most 30 seconds to complete the process.
换句话说,您的应用程序完全受制于为您安排后台获取的系统。此外,每次应用程序使用后台提取时,最多只需30秒即可完成此过程。
Include in your AppDelegate (change the below code to your fetching needs)
包含在AppDelegate中(将以下代码更改为您的提取需求)
-(void) application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
NSLog(@"Background fetch started...");
//---do background fetch here---
// You have up to 30 seconds to perform the fetch
BOOL downloadSuccessful = YES;
if (downloadSuccessful) {
//---set the flag that data is successfully downloaded---
completionHandler(UIBackgroundFetchResultNewData);
} else {
//---set the flag that download is not successful---
completionHandler(UIBackgroundFetchResultFailed);
}
NSLog(@"Background fetch completed...");
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
return YES;
}
To check if Background Refresh is enabled for your app use the below code:
要检查您的应用是否启用了后台刷新,请使用以下代码:
UIBackgroundRefreshStatus status = [[UIApplication sharedApplication] backgroundRefreshStatus];
switch (status) {
case UIBackgroundRefreshStatusAvailable:
// We can do background fetch! Let's do this!
break;
case UIBackgroundRefreshStatusDenied:
// The user has background fetch turned off. Too bad.
break;
case UIBackgroundRefreshStatusRestricted:
// Parental Controls, Enterprise Restrictions, Old Phones, Oh my!
break;
}