Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent websocket connection to drop when entering background state ios swift

Tags:

ios

swift

sockets

After doing lots of research it seems this is a grey area...

I am trying to send occasional network requests via websockets once a user has entered the background state (using the Swift library Starscream, but I believe the problem is with iOS and sockets in general). Shortly after the user leaves the app, the socket connection automatically disconnects. I gather this has something to do with Apple's policy on 3rd party network activity in the background.

In my AppDelegate, I have tried reconnecting when the user leaves, etc, but this does not work. I have also heard about workarounds involving playing audio, but apparently this will stop my app from being published to the App Store (if that's not the case, then why and how does this work?). Others say I need some sort of grant from Apple, how do I request this? It would be great for someone to clear this up, and provide a legitimate solution. I feel this is something that apps should be capable of, so I'm waiting to find a solution.

like image 291
Caspar Wylie Avatar asked May 08 '17 17:05

Caspar Wylie


3 Answers

I believe there is no legitimate way to truly work around this. Apple doesn't want apps doing stuff in the background because background activity is a big battery drain and it could make iPhone users feel their battery doesn't last enough (in addition to other issues such as "unexplained" network usage and so on), so they only provide very limited options regarding background activity in iOS apps for the sake of user experience. However, we can keep the app alive in some manners:

From the App Programming Guide for iOS:

When you find it necessary to keep your app running in the background, iOS helps you do so efficiently and without draining system resources or the user’s battery. The techniques offered by iOS fall into three categories:

  • Apps that start a short task in the foreground can ask for time to finish that task when the app moves to the background.
  • Apps that initiate downloads in the foreground can hand off management of those downloads to the system, thereby allowing the app to be suspended or terminated while the download continues.
  • Apps that need to run in the background to support specific types of tasks can declare their support for one or more background execution modes.

So it seems that, other than asking iOS to allow the app to finish short tasks or downloads, the only way to request the system to allow the app to run in the background is to specify a background execution mode in our Info.plist. This can be done in XCode's Capabilities dialog for your project, or by editing the property list file directly. Let's check which background execution modes we have available:

In iOS, only specific app types are allowed to run in the background:

  • Apps that play audible content to the user while in the background, such as a music player app
  • Apps that record audio content while in the background
  • Apps that keep users informed of their location at all times, such as a navigation app
  • Apps that support Voice over Internet Protocol (VoIP)
  • Apps that need to download and process new content regularly
  • Apps that receive regular updates from external accessories

Keeping a socket alive could fall into the "Apps that need to download and process new content regularly" use case, so let's check that:

Fetching Small Amounts of Content Opportunistically

Apps that need to check for new content periodically can ask the system to wake them up so that they can initiate a fetch operation for that content. To support this mode, enable the Background fetch option from the Background modes section of the Capabilities tab in your Xcode project. (You can also enable this support by including the UIBackgroundModes key with the fetch value in your app’s Info.plist file.) Enabling this mode is not a guarantee that the system will give your app any time to perform background fetches. The system must balance your app’s need to fetch content with the needs of other apps and the system itself. After assessing that information, the system gives time to apps when there are good opportunities to do so.

So it seems that this option is only usable for obtaining small amounts of content through HTTP requests (or other network requests), not for the kind of two-way constant communication a websocket would allow you to use. In fact, looking at other related answers, it seems there is indeed no legitimate manner to keep a socket open when the app enters background mode.

This means that, to do what you want, you cannot use websockets as your only communication channel. I'd recommend you to either use the fetch background mode (as described above) in order to fetch content in larger chunks than you would using the websocket while the app is in the background, or if you want the user to be able to see that new content is available, you could implement push notifications.

You can't use Push Notifications to send large amounts of content directly, but they can be used to prompt the user that there is new content available when they open your app. Regardless of whether you use background fetch or Push Notifications, you should implement methods on your App Delegate which will synchronize the app's state with your backend's state whenever your app is brought back from the background state.

Finally, regarding using audio as a workaround: The audio background state key will allow your app to stay alive indefinitely in the background - but if your app does not truly use it in order to play audio, it will get rejected by the app store.

like image 96
Pedro Castilho Avatar answered Oct 06 '22 23:10

Pedro Castilho


This is solution-

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()

}
like image 30
Rohit Nishad Avatar answered Oct 06 '22 23:10

Rohit Nishad


I'm not sure what your application is doing so I can promise that Apple will approve, but you'll need a Backgroundmodes entitlement to do this.

In Xcode:

enter image description here

Here is a link to the Apple Docs on adding entitlements to your application.

Here is the docs for programming background execution in iOS.

After you've enabled the entitlement in Xcode and added the needed types to your plist you should be able to start coding your background websocket connection

like image 27
Donovan King Avatar answered Oct 06 '22 23:10

Donovan King