xcode 如何解决 iOS 后台应用程序获取不工作的问题?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39197933/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to troubleshoot iOS background app fetch not working?
提问by Jason
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 中测试时它可以工作,但在设备上运行时却没有!
- My test device is running iOS 9.3.5 (my deployment target is 7.1)
- I have enabled "Background fetch" under "Background modes" under "Capabilities" on the target in Xcode
- 我的测试设备运行的是 iOS 9.3.5(我的部署目标是 7.1)
- 我在 Xcode 的目标上的“功能”下的“背景模式”下启用了“背景获取”
In application:didFinishLaunchingWithOptions I have tried various intervals with setMinimumBackgroundFetchInterval, including UIApplicationBackgroundFetchIntervalMinimum
在 application: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
我已经实现了应用程序: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
I have successfully tested with a new scheme as shown in another SO answer (https://stackoverflow.com/a/29923802/519030)
- My tests show code executing in the performFetch method in about 0.3 seconds (so it's not taking a long time)
- I have verified that the device has background refresh enabled within settings.
- Of course, I've looked at the other SO questions hoping someone else experienced the same thing. :)
我已经(成功!)使用 Xcode 的 Debug/SimulateBackgroundFetch 对模拟器和设备进行了测试
我已经成功地测试了一个新方案,如另一个 SO 答案所示(https://stackoverflow.com/a/29923802/519030)
- 我的测试显示在 performFetch 方法中执行代码大约需要 0.3 秒(因此不会花费很长时间)
- 我已验证设备在设置中启用了后台刷新。
- 当然,我已经查看了其他 SO 问题,希望其他人也经历过同样的事情。:)
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 处理程序,还编写了发送本地通知的代码。
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 似乎触发了 fetch 三次,每次大约相隔十五分钟,但之后就再也没有发生过。
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!
感谢任何建议!
采纳答案by Paulw11
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
我的示例应用程序在这里
回答by TechSeeko
In order to implement background fetch there are three things you must do:
为了实现后台提取,您必须做三件事:
- Check the box Background fetch in the Background Modes of your app's Capabilities.
- Use setMinimumBackgroundFetchInterval(_:) to set a time interval appropriate for your app.
- Implement application(_:performFetchWithCompletionHandler:) in your app delegate to handle the background fetch.
- 选中应用程序功能的后台模式中的后台获取框。
- 使用 setMinimumBackgroundFetchInterval(_:) 设置适合您的应用程序的时间间隔。
- 在您的应用程序委托中实现 application(_:performFetchWithCompletionHandler:) 以处理后台提取。
Background Fetch Frequency
后台获取频率
How frequent our application can perform Background Fetch is determined by the system and it depends on:
我们的应用程序执行后台提取的频率由系统决定,它取决于:
- Whether network connectivity is available at that particular time
- Whether the device is awake i.e. running
- How much time and data your application has consumed in the previous Background Fetch
- 在特定时间网络连接是否可用
- 设备是否唤醒即运行
- 您的应用程序在之前的后台获取中消耗了多少时间和数据
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;
}