[GH-ISSUE #308] Add support for background URLSessions #187

Open
opened 2026-03-03 16:46:31 +03:00 by kerem · 14 comments
Owner

Originally created by @alazaro on GitHub (Nov 10, 2016).
Original GitHub issue: https://github.com/OAuthSwift/OAuthSwift/issues/308

Description:

The App Programming Guide for watchOS recommends the use of background sessions when making network-based operations.

The library uses URLSessionConfiguration.default by default, but it would be good to be able to use URLSessionConfiguration.background(withIdentifier:) when needed.

Originally created by @alazaro on GitHub (Nov 10, 2016). Original GitHub issue: https://github.com/OAuthSwift/OAuthSwift/issues/308 ### Description: The [App Programming Guide for watchOS](https://developer.apple.com/library/content/documentation/General/Conceptual/WatchKitProgrammingGuide/iOSSupport.html#//apple_ref/doc/uid/TP40014969-CH21-SW1) recommends the use of background sessions when making network-based operations. The library uses `URLSessionConfiguration.default` by default, but it would be good to be able to use [`URLSessionConfiguration.background(withIdentifier:)`](https://developer.apple.com/reference/foundation/urlsessionconfiguration/1407496-background) when needed.
Author
Owner

@phimage commented on GitHub (Nov 10, 2016):

I add a global configuration object OAuthSwift.session where you can change the configuration (URLSessionConfiguration)

<!-- gh-comment-id:259630330 --> @phimage commented on GitHub (Nov 10, 2016): I add a global configuration object `OAuthSwift.session` where you can change the `configuration` (`URLSessionConfiguration`)
Author
Owner

@alazaro commented on GitHub (Nov 10, 2016):

That was fast! Thanks!!

<!-- gh-comment-id:259648749 --> @alazaro commented on GitHub (Nov 10, 2016): That was fast! Thanks!!
Author
Owner

@antwerpenR commented on GitHub (Nov 15, 2016):

Thanks for adding this - just what I was looking for too. Can you help me to know how I should configure background session? I am trying with
let config = URLSessionConfiguration.background(withIdentifier: "bgConfig") OAuthSwift.session.configuration = config

but this will not compile - it gives me error " 'configuration' is inaccessible due to 'internal' protection level.

From a bit of research, I think it might be related to this: http://taylorfranklin.me/2016/07/10/the-default-memberwise-initializers-headache/

After a bit of experimentation, I was able to proceed by setting the structure variables also to "public" in your code for OAuthSwift (so maybe this is a bug - or I am doing it wrong?)

`extension OAuthSwift {

// configure how URLSession is initialized
public struct Session {
    public var configuration = URLSessionConfiguration.default
    public var queue = OperationQueue.main
    // An optional delegate for the URLSession
    public var delegate: URLSessionDelegate?

    // Monitor session: see UIApplication.shared.isNetworkActivityIndicatorVisible
    public var isNetworkActivityIndicatorVisible = true

    func newURLSession() -> URLSession {
        return URLSession(configuration: self.configuration, delegate: self.delegate, delegateQueue: self.queue)
    }
}
public static var session = Session()

}
`

...which lead to my next question. I do not want all my sessions to be background - only those for which I am doing multi-part/form upload (photos). How to do this since I think this is global? I guess that I need to create another OAuth2Swift instance and set the session only on it....is that correct?

<!-- gh-comment-id:260590463 --> @antwerpenR commented on GitHub (Nov 15, 2016): Thanks for adding this - just what I was looking for too. Can you help me to know how I should configure background session? I am trying with `let config = URLSessionConfiguration.background(withIdentifier: "bgConfig") OAuthSwift.session.configuration = config` but this will not compile - it gives me error " 'configuration' is inaccessible due to 'internal' protection level. From a bit of research, I think it might be related to this: http://taylorfranklin.me/2016/07/10/the-default-memberwise-initializers-headache/ After a bit of experimentation, I was able to proceed by setting the structure variables also to "public" in **your** code for OAuthSwift (so maybe this is a bug - or I am doing it wrong?) `extension OAuthSwift { ``` // configure how URLSession is initialized public struct Session { public var configuration = URLSessionConfiguration.default public var queue = OperationQueue.main // An optional delegate for the URLSession public var delegate: URLSessionDelegate? // Monitor session: see UIApplication.shared.isNetworkActivityIndicatorVisible public var isNetworkActivityIndicatorVisible = true func newURLSession() -> URLSession { return URLSession(configuration: self.configuration, delegate: self.delegate, delegateQueue: self.queue) } } public static var session = Session() ``` } ` ...which lead to my next question. I do not want all my sessions to be background - only those for which I am doing multi-part/form upload (photos). How to do this since I think this is global? I guess that I need to create another OAuth2Swift instance and set the session only on it....is that correct?
Author
Owner

@phimage commented on GitHub (Nov 15, 2016):

I do it to fast maybe. Not tested yet. So maybe there is an access problem.

Then I do it in a static way so there is no way to configure by OAuthSwift object or even by request

I will edit to be editable by OAuthSwift

<!-- gh-comment-id:260627725 --> @phimage commented on GitHub (Nov 15, 2016): I do it to fast maybe. Not tested yet. So maybe there is an access problem. Then I do it in a static way so there is no way to configure by OAuthSwift object or even by request I will edit to be editable by OAuthSwift
Author
Owner

@antwerpenR commented on GitHub (Nov 15, 2016):

@phimage - yes - I think there is an access problem. It does seem that my fix works for me...when I set the elements of the struct as public I can set them OK from my code - so do not be too quick to implement it some other way!

Still - it is important for me to have some sessions working with default settings (so that I can use callbacks) and others with background settings (which need to work with delegate methods)....so it is important that any implementation allows this configuration. Hope this helps and I am happy to help test anything going forward!

<!-- gh-comment-id:260653273 --> @antwerpenR commented on GitHub (Nov 15, 2016): @phimage - yes - I think there is an access problem. It does seem that my fix works for me...when I set the elements of the struct as public I can set them OK from my code - so do not be too quick to implement it some other way! Still - it is important for me to have some sessions working with default settings (so that I can use callbacks) and others with background settings (which need to work with delegate methods)....so it is important that any implementation allows this configuration. Hope this helps and I am happy to help test anything going forward!
Author
Owner

@phimage commented on GitHub (Nov 22, 2016):

an other big change has been done to be able to update it by OAuthSwift instance,
more precisely in OAuthSwiftClient instance oauthSwift.client.sessionFactory

You can create a new one or assigning one instance of your choice

oauthSwift.client.sessionFactory = URLSessionFactory(....)

or you can update it

oauthSwift.client.sessionFactory.configuration = ...

N.B.: This URLSessionFactory (from client instance) is passed to request Config object

<!-- gh-comment-id:262208651 --> @phimage commented on GitHub (Nov 22, 2016): an other big change has been done to be able to update it by `OAuthSwift` instance, more precisely in `OAuthSwiftClient` instance `oauthSwift.client.sessionFactory` You can create a new one or assigning one instance of your choice ```swift oauthSwift.client.sessionFactory = URLSessionFactory(....) ``` or you can update it ```swift oauthSwift.client.sessionFactory.configuration = ... ``` N.B.: This `URLSessionFactory` (from client instance) is passed to request `Config` object
Author
Owner

@antwerpenR commented on GitHub (Nov 25, 2016):

Thanks for this update....but I am having problems using it - I get runtime error:
2016-11-25 10:47:25.746 API-Sandbox[66217:5638450] *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Completion handler blocks are not supported in background sessions. Use a delegate instead.'

My calling code is:
`....
backgroundOauthswift.client.sessionFactory.configuration = URLSessionConfiguration.background(withIdentifier: "meetupApp")
backgroundOauthswift.client.sessionFactory.delegate = self
backgroundOauthswift.client.postMultiPartRequest(
requiredURL,
method: .POST,
parameters: ["sign":"true", "photo-host": "secure"],
headers: [:],
multiparts: [multipart],
checkTokenExpiration: true,
success: nil,
failure: nil
)
dismiss(animated: true, completion: nil)
}
}

func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
print("DID FINISH: urlSessionDidFinishEvents, (session.description)")
}
`

I think I am NOT passing closures since I set success and failure to nil instead
I have set my calling class as URLSessionDelegate and provided the three optional methods...
What am I doing wrong?

<!-- gh-comment-id:262871180 --> @antwerpenR commented on GitHub (Nov 25, 2016): Thanks for this update....but I am having problems using it - I get runtime error: `2016-11-25 10:47:25.746 API-Sandbox[66217:5638450] *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Completion handler blocks are not supported in background sessions. Use a delegate instead.'` My calling code is: `.... backgroundOauthswift.client.sessionFactory.configuration = URLSessionConfiguration.background(withIdentifier: "meetupApp") backgroundOauthswift.client.sessionFactory.delegate = self backgroundOauthswift.client.postMultiPartRequest( requiredURL, method: .POST, parameters: ["sign":"true", "photo-host": "secure"], headers: [:], multiparts: [multipart], checkTokenExpiration: true, success: nil, failure: nil ) dismiss(animated: true, completion: nil) } } func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) { print("DID FINISH: urlSessionDidFinishEvents, \(session.description)") } ` I think I am NOT passing closures since I set `success` and `failure` to nil instead I have set my calling class as URLSessionDelegate and provided the three optional methods... What am I doing wrong?
Author
Owner

@phimage commented on GitHub (Nov 25, 2016):

OAuthSwift use the builded session with a completion handler, maybe not authorized in background session...
"self.session.dataTask(with: usedRequest) { (data, resp, error) in"

<!-- gh-comment-id:262892407 --> @phimage commented on GitHub (Nov 25, 2016): OAuthSwift use the builded session with a completion handler, maybe not authorized in background session... "self.session.dataTask(with: usedRequest) { (data, resp, error) in"
Author
Owner

@antwerpenR commented on GitHub (Nov 25, 2016):

So...indeed, this looks like a bug?

data task convenience methods. These methods create tasks that * **bypass the normal delegate calls for response and data delivery**, * and provide a simple cancelable asynchronous interface to receiving * data. Errors will be returned in the NSURLErrorDomain, * see <Foundation/NSURLError.h>. The delegate, if any, will still be * called for authentication challenges..

Can you do a fix or point me to how to avoid the problem?

<!-- gh-comment-id:262904104 --> @antwerpenR commented on GitHub (Nov 25, 2016): So...indeed, this looks like a bug? `data task convenience methods. These methods create tasks that * **bypass the normal delegate calls for response and data delivery**, * and provide a simple cancelable asynchronous interface to receiving * data. Errors will be returned in the NSURLErrorDomain, * see <Foundation/NSURLError.h>. The delegate, if any, will still be * called for authentication challenges..` Can you do a fix or point me to how to avoid the problem?
Author
Owner

@phimage commented on GitHub (Nov 25, 2016):

That's a lot of change and I have not time to do it now

  • OAuthSwitHttpRequest must implement URLSessionDataDelegate
  • "self.session.dataTask(with: usedRequest) { (data, resp, error) in" must become "self.session.dataTask(with: usedRequest) " and all the code completionhandler must be executed into the OAuthSwitHttpRequest URLSessionDataDelegate implementation
  • the delegate provided by user to build session must be replace by OAuthSwitHttpRequest. But in OAuthSwitHttpRequest URLSessionDataDelegate implementation, all functions could be mapped to delegate provided by user
<!-- gh-comment-id:262916918 --> @phimage commented on GitHub (Nov 25, 2016): That's a lot of change and I have not time to do it now - OAuthSwitHttpRequest must implement URLSessionDataDelegate - "self.session.dataTask(with: usedRequest) { (data, resp, error) in" must become "self.session.dataTask(with: usedRequest) " and all the code completionhandler must be executed into the OAuthSwitHttpRequest URLSessionDataDelegate implementation - the delegate provided by user to build session must be replace by OAuthSwitHttpRequest. But in OAuthSwitHttpRequest URLSessionDataDelegate implementation, all functions could be mapped to delegate provided by user
Author
Owner

@antwerpenR commented on GitHub (Nov 25, 2016):

Thanks - I think I need to wait till you have time to do this properly - it goes a bit beyond my current skill and knowledge level:

I tried modifying your code:
if self.session.delegate == nil { //do all the existing code as normal with callbacks } else { self.task = self.session.dataTask(with: usedRequest) }

But I get then called with the delegate call didReceive challenge: completionHandler and I am not sure how to handle that properly...although it does work for the other foreground calls...

<!-- gh-comment-id:262917505 --> @antwerpenR commented on GitHub (Nov 25, 2016): Thanks - I think I need to wait till you have time to do this properly - it goes a bit beyond my current skill and knowledge level: I tried modifying your code: `if self.session.delegate == nil { //do all the existing code as normal with callbacks } else { self.task = self.session.dataTask(with: usedRequest) }` But I get then called with the delegate call `didReceive challenge: completionHandler` and I am not sure how to handle that properly...although it does work for the other foreground calls...
Author
Owner

@phimage commented on GitHub (Jun 25, 2018):

After a very long time (sorry) I add useDataTaskClosure in URLSessionFactory
If false, then we call self.task = self.session.dataTask(with: usedRequest) without the closure

It is not sufficient because in delegate you have to do a lot of work, same as OAutSwift framework do
A first step maybe is to call OAuthSwiftHTTPRequest.completionHandler but success and failure handler are overriden internally ( to extract token before calling the handler passed by developper)

<!-- gh-comment-id:399911930 --> @phimage commented on GitHub (Jun 25, 2018): After a very long time (sorry) I add `useDataTaskClosure` in `URLSessionFactory` If false, then we call `self.task = self.session.dataTask(with: usedRequest)` without the closure It is not sufficient because in `delegate` you have to do a lot of work, same as OAutSwift framework do A first step maybe is to call `OAuthSwiftHTTPRequest.completionHandler` but success and failure handler are overriden internally ( to extract token before calling the handler passed by developper)
Author
Owner

@CuriousDev21 commented on GitHub (May 12, 2021):

Any update on this matter? I also have to keep requests running in background. Looking for an example with OAuthSwift.
Thanks in advance.

<!-- gh-comment-id:839689095 --> @CuriousDev21 commented on GitHub (May 12, 2021): Any update on this matter? I also have to keep requests running in background. Looking for an example with OAuthSwift. Thanks in advance.
Author
Owner

@phimage commented on GitHub (May 12, 2021):

no
if you really need it and have code to test background session, you could make PR

<!-- gh-comment-id:839707566 --> @phimage commented on GitHub (May 12, 2021): no if you really need it and have code to test background session, you could make PR
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/OAuthSwift#187
No description provided.