1

I'm trying to upload data using NSURLSession with a background task from an OSX share extension.

As soon as I start the task, by delegate is called back with the world's least helpful error message:

The operation couldn’t be completed. (NSURLErrorDomain error -995.)

There's no other information in the NSError object, nor in the console.

After scouring the internet, the only clue I have is to make sure that I've set up the configuration.sharedContainerIdentifier correctly, however I've already done that:

let configuration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(uniqueId)
configuration.sharedContainerIdentifier = Config.appGroupName
urlSession = NSURLSession.init(configuration: configuration, delegate: self, delegateQueue: nil)

I then prepare the request and create the task:

let task = self.urlSession!.dataTaskWithRequest(request)
self.tasks.append(task)
task.resume()

Note that everything works perfectly when from my main app. It's just the sharing extension that fails.

What other problems could cause error -995?

tarmes
  • 15,366
  • 10
  • 53
  • 87
  • How about [this question](http://stackoverflow.com/questions/26172783/upload-nsurlsesssion-becomes-invalidated-in-sharing-extension-in-ios8-with-error)? – trojanfoe Apr 12 '16 at 14:38
  • That just tells me to set the `sharedContainerIdentifier` , which I've already stated that I have done. – tarmes Apr 12 '16 at 14:45
  • File a bug with Apple. That error code should be documented, and isn't. Only somebody at Apple can tell you what it means, in all likelihood, unless somebody just happens to have stumbled across it. – dgatwood May 01 '16 at 07:18

1 Answers1

1

(Bear with me, I don't have rep yet for a comment so I have to answer, and notwithstanding the possibility you've found a bug, the last part I'm adding might help others)

When you're using an NSURLSession any background upload/download that completes will launch your main application. This is detailed in the Common Scenarios page.

As you show you've done, you need to set the NSURLSessionConfiguration with a sharedContainerIdentifier:

let configuration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(uniqueId)
configuration.sharedContainerIdentifier = Config.appGroupName
urlSession = NSURLSession.init(configuration: configuration, delegate: self, delegateQueue: nil)

You also need to ensure that both your app extension and your main app are all triple-checked in the 'App Groups' option in Xcode (I'm looking at Xcode 8.1), which means:

  • The app group name is the same for both app and extension(!)
  • the check beside Add the App Group entitlements in the entitlement file
  • the check beside Add the App Groups feature to your App ID
  • the check beside Add App Groups to your App ID

And the checks need to be there on both app and extension.

I'm a bit rusty on adding these but my recollection is these aren't listed in the right order, e.g. you have to 'Add App Groups to your ID' before you can check them in both app and extension, but I could be wrong.

Advanced - Thirdparty libraries in app extensions.

Looking at the github project nst/STTwitter, a third-party Twitter REST library, they needed to add a configurable group ID to allow setting the NSURLSessionConfiguration sharedContainerIdentifier to the library user's group ID.

I may again be mistaken but this seems like it would be a general problem when using thirdparty libraries in an app extension - e.g. I've just now come across this error when trying to use the ChromeCast SDK and this could be the kick in the pants.

J. Longman
  • 101
  • 1
  • 7
  • What is Config.appGroupName here? – jrwren Aug 23 '17 at 21:02
  • 1
    Generally speaking this is a group identifier that you use to link the disparate codebases programmatically. E.g. if you have an extension that adds a row to a database that you then read in your app, they both must have the same appGroupName, as well as having app groups enabled. – J. Longman Aug 28 '17 at 18:23
  • 1
    Specifically in the extension/download case a download is delegated to this appGroup identifier and accounted with your app instead of your extension. This is necessary since your extension and apps have different lifecycles and extensions are simpler as well as more likely to be killed. – J. Longman Aug 28 '17 at 18:25