3 Replies
      Latest reply: Oct 14, 2016 1:13 AM by eskimo RSS
      ronak2121 Level 1 Level 1 (0 points)

        Hi,

         

        I'm building a framework that creates a URLSession with a backgroundTransfer configuration. We have settings that allow our callers to switch settings on our session.

         

        When these settings change, we proceed to invalidate the session, wait for the delegate callback (session:didInvalidateWithError:) and proceed to recreate the session with the new settings within that callback.

         

        I'm trying to write a unit test to test this code and I see that it's not working. I keep getting messages in the console that a background session already exists with that identifier. I've tried nilling out my reference to the session, dispatch asyncing and dispatch_after for the recreation calls.

         

        Is it just not possible to change the settings of a background transfer session's URLSessionConfiguration?

         

        Thanks

        • Re: Background Transfer Session Recreation
          eskimo Apple Staff Apple Staff (5,995 points)

          The technique you’ve described should work.  And, in fact, it’s working for me.  I created a simple test project from the Single View Application template, configured the code as shown below, wired up a Test button to testAction(_:), and this is what gets printed:

          … set up session
          … will invalidate session
          … invalidate, no error
          … set up session
          

          The first line is emitted on launch and the remaining lines when I tap the Test button.

          This is Xcode 8 on 10.11.6 targeting iOS 10.0.2.

          Please try this test and let me know what you see.

          Share and Enjoy

          Quinn “The Eskimo!”
          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
          let myEmail = "eskimo" + "1" + "@apple.com"

          class ViewController: UIViewController, URLSessionDelegate {
          
              var session: URLSession? = nil
          
              func setupSession() {
                  NSLog("set up session")
                  let config = URLSessionConfiguration.background(withIdentifier: "com.example.apple-samplecode.xxsi")
                  self.session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)
              }
          
              override func viewDidLoad() {
                  super.viewDidLoad()
                  setupSession()
              }
          
              @IBAction func testAction(_ sender: AnyObject) {
                  if let session = self.session {
                      NSLog("will invalidate session")
                      self.session = nil
                      session.invalidateAndCancel()
                  } else {
                      NSLog("no session to invalidate")
                  }
              }
          
              func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
                  if let error = error as? NSError {
                      NSLog("invalidate, error %@ / %d", error.domain, error.code)
                  } else {
                      NSLog("invalidate, no error")
                  }
                  self.setupSession()
              }
          }
          
            • Re: Background Transfer Session Recreation
              ronak2121 Level 1 Level 1 (0 points)

              Hi Quinn,

               

              Thanks for the quick reply!

               

              I've been meaning to post my code so you can see what I'm doing:

               

              func recreateSession(with configuration: URLSessionConfiguration, completionHandler: (() -> Void)?) {
                      sessionDelegate.sessionDidBecomeInvalidWithError = { [weak self] session, error in
                          guard let strongSelf = self else { return }
              
                          strongSelf.sessionDelegate = SessionDelegate()
                          strongSelf.session = URLSession(configuration: configuration, delegate: strongSelf.sessionDelegate, delegateQueue: strongSelf.delegateQueue)
              
                          completionHandler?()
                      }
              
                      session.finishTasksAndInvalidate()
              }
              
              
              func updateAllowOverCellularMode(to allowOverCellularMode: Bool, completionHandler: (() -> Void)?) {
                      let configuration = sessionConfiguration
                      configuration.allowsCellularAccess = allowOverCellularMode
                      self.recreateSession(with: configuration, completionHandler: completionHandler)
              }
              
              
              

               

              I'm testing this code like so:

               

              fileprivate let sessionConfiguration: URLSessionConfiguration = URLSessionConfiguration.background(withIdentifier: "identifier")
              
              
              override func setUp() {
                      super.setUp()
                      sessionManager = DefaultBackgroundTransferURLSessionManager(configuration: sessionConfiguration, maxRequestConcurrencyCount: maxRequestConcurrencyCount, qualityOfService: qualityOfService)
                  }
              
              override func tearDown() {
                   super.tearDown()
                   sessionManager = nil
              }
              
              func testUpdateAllowOverCellularMode() {
                      guard let sessionManager = sessionManager else { XCTFail(); return }
              
                        let testExpectation = expectation(description: "Session Recreation Expectation")
                           sessionManager.updateAllowOverCellularMode(to: true) {
                               XCTAssertTrue(sessionManager.allowOverCellularMode)
                               XCTAssertTrue(sessionManager.sessionConfiguration.allowsCellularAccess)
                               testExpectation.fulfill()
                      }
                      waitForExpectations(timeout: 5) { error in
                          XCTAssertNil(error, "We encountered an error?")
                      }
                  }
              
              
              

               

              This test only passes sometimes....sometimes it hangs and I get this printed in the console:

               

              2016-10-13 17:24:12.352 xctest[66734:54590554] A background URLSession with identifier identifier already exists!

                • Re: Background Transfer Session Recreation
                  eskimo Apple Staff Apple Staff (5,995 points)

                  Alas this isn’t really enough to go on.  To start, you’re using a bunch of stuff (for example, SessionDelegate) that you don’t explain.

                  Two suggestions:

                  • Check that your delegate queue is a serial queue.  I’ve seen some very odd things happen if you use a concurrent queue for NSURLSession.

                  • Put my code into an XCTest to see if the issue is tied to the XCTest environment.

                  Share and Enjoy

                  Quinn “The Eskimo!”
                  Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                  let myEmail = "eskimo" + "1" + "@apple.com"