iOS 8今日小部件使用一段时间后停止工作

22
我为德国冰球联赛DEL制作了一个今日小部件。
我从我们的服务器加载下一场比赛并在tableView中显示它们。加载过程是在建议的方法"widgetPerformUpdateWithCompletionHandler"中启动的。最初,我在"viewWillAppear"中加载一些缓存数据。
到目前为止,一切运行得很好!
但是过一段时间(一天),小部件就停止工作了。当我打开通知中心时,小部件看起来正常,但它再也没有更新。我必须将小部件从通知中心中删除,然后重新添加。之后,小部件会再次工作一天,然后再次停止工作。
为了查看小部件在做什么,我在"widgetPerformUpdateWithCompletionHandler"中加载数据时添加了一个简单的白色视图和状态文本,以查看小部件是否在工作。当小部件正在工作时,白色视图会出现。当它不工作时,状态视图不会出现。所以我认为"widgetPerformUpdateWithCompletionHandler"方法在小部件在通知中心活跃一段时间后不再被调用。
我不知道是什么导致小部件停止工作。有任何想法吗?
4个回答

19

我遇到了同样的问题,我在网络请求后立即调用completionHandler(NCUpdateResultNoData);来解决它,即使响应尚未返回。我发现如果不调用完成处理程序,widgetPerformUpdateWithCompletionHandler将不再被调用,因此不会有任何更新。还要确保在请求调用返回后的所有分支中调用完成处理程序。


是的,我也发现了这个问题。我尝试了一些方法并发现当网络请求需要一些时间(网络条件不佳)时,widgetPerformUpdateWithCompletionHandler 永远不会再次被调用。我认为完成处理程序在某种程度上变得无效了。你的解决方案可以解决问题,但这不是预期的方式。我通过根本不使用 widgetPerformUpdateWithCompletionHandler 来解决了这个问题。希望这个问题在未来得到解决。 - Wilko X
那么如果不使用widgetPerformUpdateWithCompletionHandler,您是在其他地方调用网络请求吗? - Si Yi Cathy Meng
是的,我在viewDidAppear中调用网络请求。因此小部件不会在后台刷新,而是每次用户打开它时都会刷新。 - Wilko X
调用viewDidAppear上的update不是更新小部件的正确方式(至少不是苹果建议的)。即使使用widgetPerformUpdateWithCompletionHandler,每次打开小部件时都会刷新小部件。 - valvoline
3
我也遇到了同样的问题。我在 widgetPerformUpdateWithCompletionHandler 方法中复制了一个块:self.completionHandler = _CompletionHandler;,然后在异步任务完成后调用它。但是经过一段时间(几个小时?),小部件的 viewDidLoad 被调用,但 widgetPerformUpdateWithCompletionHandler 不再被调用。因此,我的小部件看起来为空,高度约为50像素。复制的块是否不安全?或者我应该在 viewDidLoad 中开始我的异步任务? - Raphaël Pinto
这肯定不是它的预期使用方式,对吧? - bencallis

13

正如其他人所提到的,这是由于在调用widgetPerformUpdateWithCompletionHandler后未先调用completionHandler导致的。你需要确保无论如何都会调用completionHandler

我建议的处理方法是将completionHandler保存为实例变量,然后在viewDidDisappear:中使用failed调用它:

@property(nonatomic, copy) void (^completionHandler)(NCUpdateResult);
@property(nonatomic) BOOL hasSignaled;

- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
    self.completionHandler = completionHandler;
    // Do work.
}

- (void)viewDidDisappear:(BOOL)animated {
  [super viewDidDisappear:animated];
  if (!self.hasSignaled) [self signalComplete:NCUpdateResultFailed];
}

- (void)signalComplete:(NCUpdateResult)updateResult {
  NSLog(@"Signaling complete: %lu", updateResult);
  self.hasSignaled = YES;
  if (self.completionHandler) self.completionHandler(updateResult);
}

我尝试了这个方法,因为它似乎是一个明智的解决方案,以确保完成块始终被调用。不幸的是,使用这种方法并不能完全解决问题。 - bencallis
真希望我能在这里赠送金牌,就像在 Reddit 上一样。 - maksa
每次调用widgetPerformUpdateWithCompletionHandler时,您是否还需要将hasSignaled重置为NO? - RealCasually

3

另一个问题是,一旦停止调用 widgetPerformUpdateWithCompletionHandler:,将不会再有其他完成处理程序要调用。我没有找到让系统再次调用 widgetPerformUpdateWithCompletionHandler: 的方法。因此,请确保您的小部件还将尝试通过 viewWillAppear: 重新加载数据作为备选方案,否则某些用户可能会陷入无法加载小部件的困境。


1
遗憾的是,这就是我最终所做的事情,很遗憾即使在iOS 9中尝试遵循widgetPerformUpdateWithCompletionHandler的正确使用也不起作用。谢谢您的建议,这是唯一真正解决了我的问题的方法。 - Polar Bear
你是如何处理这个的?我猜想如果没有一个适当的完成句柄,系统将永远不知道小部件何时恢复操作,因此仅依赖于viewWillAppear调用?你是否进行任何同步,以确保在正常运行时事物不会重复更新两次? - RealCasually

1
在 widgetPerformUpdateWithCompletionHandler 中使用 NCUpdateResultNewData 调用 completionHandler,在异步调用返回之前再次使用 NCUpdateResultNewData 或 NCUpdateResultFailed 调用它似乎是有效的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接