防止 WebSocket 在进入后台状态时断开连接 iOS Swift

21

经过大量研究,似乎这是一个灰色地带......

我正在尝试在用户进入后台状态后通过WebSockets发送偶尔的网络请求(使用Swift库Starscream,但我认为问题与iOS和套接字有关)。用户离开应用程序后不久,套接字连接会自动断开。我了解到这与苹果关于第三方网络活动在后台的政策有关。

在我的AppDelegate中,我尝试了当用户离开时重新连接等操作,但这并不起作用。我也听说过涉及播放音频的解决方法,但显然这将使我的应用程序无法发布到App Store(如果不是这种情况,那么为什么以及如何实现?)其他人说我需要获得来自Apple的某种授权,我该如何请求?有人能够澄清这个问题并提供合法的解决方案吗?我觉得这是应用程序应该具备的功能,所以我一直在等待找到解决方案。


你找到解决方案了吗?我也遇到了同样的问题。 - FH-
嗨,Caspar Wylie,你能找到背景模式问题的正确解决方案吗?或者如果你可以分享一下你尝试的其他解决方案作为替代方法的经验,谢谢!我正在使用 GFayeSwift 库,它在底层使用 Startscream 库,也遇到了同样的问题。 - Dhaval H. Nena
我记得曾经推迟了我打算用这个功能的开发,不幸的是我从来没有找到一个可靠的解决方案。现在已经过去了4年,我不知道现在有哪些可能性,我想在IOS领域已经发生了很多变化。 - Caspar Wylie
4个回答

32
我认为没有合法的方法可以真正解决这个问题。苹果不希望应用在后台执行操作,因为后台活动会大量消耗电池,并可能使iPhone用户感到电池续航时间不够长(除了其他问题,如“未经解释”的网络使用等),因此他们仅针对用户体验提供非常有限的iOS应用程序后台活动选项。然而,我们可以通过某些方式保持应用程序处于活动状态:
来自iOS应用程序编程指南
当您需要让应用程序在后台运行时,iOS 可以帮助您高效地实现此目的,而不会消耗系统资源或用户的电池电量。iOS 提供的技术可以分为三类:
- 在前台启动短任务的应用程序可以在应用程序进入后台时请求时间以完成该任务。 - 在前台启动下载的应用程序可以将这些下载的管理权交给系统,从而允许应用程序在下载继续进行时被挂起或终止。 - 需要在后台运行以支持特定类型任务的应用程序可以声明其支持一个或多个后台执行模式。
因此,除了请求 iOS 允许应用程序完成短任务或下载之外,请求系统允许应用程序在后台运行的唯一方法是在我们的 Info.plist 中指定后台执行模式。这可以通过 XCode 项目的 Capabilities 对话框或直接编辑属性列表文件来完成。让我们检查一下有哪些后台执行模式可用:
在iOS中,只有特定类型的应用程序被允许在后台运行:
  • 播放声音内容给用户听的应用程序,例如音乐播放器应用程序
  • 在后台录制音频内容的应用程序
  • 随时通知用户其位置的应用程序,例如导航应用程序
  • 支持VoIP(互联网语音)的应用程序
  • 需要定期下载和处理新内容的应用程序
  • 从外部配件接收定期更新的应用程序
保持套接字活动可能属于“需要定期下载和处理新内容的应用程序”用例,因此让我们检查一下:

机会性地获取少量内容

需要定期检查新内容的应用程序可以请求系统唤醒它们,以便它们可以启动获取该内容的操作。为支持此模式,请从Xcode项目的功能选项卡中的背景模式部分启用后台获取选项。(您还可以通过在应用程序的Info.plist文件中使用具有获取值的UIBackgroundModes键来启用此支持。)启用此模式不能保证系统将给予您的应用程序任何时间来执行后台获取。系统必须平衡您的应用程序获取内容的需求与其他应用程序和系统本身的需求。在评估了那些信息之后,系统会在有良好机会时给予应用程序时间。

因此,看起来这个选项只能通过HTTP请求(或其他网络请求)获取少量内容,而不能像websocket一样进行双向持续通信。实际上,从其他相关答案中看来,似乎没有合法的方法在应用程序进入后台模式时保持套接字打开。

这意味着,为了实现你想要的功能,你不能仅使用websockets作为通信渠道。我建议你要么使用fetch背景模式(如上所述)以便在应用程序处于后台时获取比使用websocket更大的内容块,要么如果你希望用户能够看到新内容可用,可以实现推送通知。无论你使用后台获取还是推送通知,你都应该在App Delegate上实现方法,以便在将应用程序从后台状态带回时将应用程序的状态与后端的状态同步。
最后,关于使用音频作为解决方法: audio 后台状态密钥将允许您的应用程序无限期地保持在后台运行状态 - 但如果您的应用程序不真正使用它来播放音频,它将被应用商店拒绝。

感谢您提供深入的答案。 - Caspar Wylie
有没有想法,如果我想每小时进行一次单个套接字请求?这样就不会频繁/持续地通信。 - Caspar Wylie

4
这是解决方案-
var backgroundUpdateTask: UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier(rawValue: 0)
func endBackgroundUpdateTask() {
    UIApplication.shared.endBackgroundTask(self.backgroundUpdateTask)
    self.backgroundUpdateTask = UIBackgroundTaskIdentifier.invalid
}
func applicationWillResignActive(_ application: UIApplication) {
    self.backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {
        self.endBackgroundUpdateTask()
    })
}
func applicationDidBecomeActive(_ application: UIApplication) {
    self.endBackgroundUpdateTask()

}

这对我有用,但你能解释一下这段代码吗?我不明白为什么它有效? - Hamed
这不是苹果推荐的使用方式。请参考https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background/extending_your_app_s_background_execution_time。 - ThE uSeFuL

1
我不确定您的应用程序具体功能,因此无法保证苹果公司会批准,但是您需要一个Backgroundmodes许可来实现这一点。
在Xcode中:

enter image description here

这里是苹果文档关于如何给你的应用程序添加权限的链接。

这里是iOS后台执行编程文档。

在你在Xcode中启用权限并将所需类型添加到你的plist文件后,你应该可以开始编写后台websocket连接的代码了。


不幸的是,即使添加后台模式也无法保持套接字连接的活动状态。后台模式只允许iOS在决定时短暂唤醒应用程序,并且套接字连接需要应用程序无限期地保持在后台运行。 - Pedro Castilho
@PedroCastilho 哦,我明白了。是的,应用程序绝对不允许无限期地保持后台模式,因此您将无法使用该方法。请问您尝试通过套接字连接发送什么?您是否可以通过推送通知完成相同的事情? - Donovan King
我不是楼主 :) 你应该将这个作为评论添加到问题中。 - Pedro Castilho

0
  • 如果您只有AppDelegate,请使用this

  • 如果您有SceneDelegate,请使用我的答案:

    var backgroundUpdateTask: UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier(rawValue: 0)
    
    func sceneDidBecomeActive(_ scene: UIScene) {
        self.endBackgroundUpdateTask()
    }
    
    func sceneWillResignActive(_ scene: UIScene) {
        self.backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {
          self.endBackgroundUpdateTask()
        })
    }
    
    func endBackgroundUpdateTask() {
        UIApplication.shared.endBackgroundTask(self.backgroundUpdateTask)
        self.backgroundUpdateTask = UIBackgroundTaskIdentifier.invalid
    }
    

这不是苹果推荐的使用方式。请参阅https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background/extending_your_app_s_background_execution_time。 - ThE uSeFuL
@ThEuSeFuL 你好,我按照https://learn.microsoft.com/en-us/xamarin/ios/app-fundamentals/backgrounding/ios-backgrounding-techniques/ios-backgrounding-with-tasks的指导进行操作,但只有在调试模式下才能正常工作,发布模式下无法运行。 - user2801184

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