8

Thanks to Apple my iOS 9 Project 'Swift 2.3' is completely unusable with iOS 10's 'Swift 3'...

I fixed almost everything except that I am having issue with using NSURLSession, Xcode is telling me that it has been renamed to URLSession, if I rename it Xcode will tell me:

use of undeclared type URLSession

Foundation is imported.

What is the issue?!

For example I am using it this way...

lazy var defaultSession: URLSession = {
    let configuration = URLSessionConfiguration.background(withIdentifier: "reCoded.BGDownload")
    configuration.sessionSendsLaunchEvents = true
    configuration.isDiscretionary = true
    let session = URLSession(configuration: configuration, delegate: self, delegateQueue, queue: nil)
    return session
}()

and even with the delegate methods the same issue.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Raffi
  • 699
  • 1
  • 8
  • 19
  • @EricD I have updated the description, if u can see it please. – Raffi Jun 14 '16 at 15:54
  • 1
    No problem with URLSession in your code for me. Seems ridiculous, I know, but... are you sure you're using Xcode 8? :) If yes, there may be a conflicting declaration somewhere... – Eric Aya Jun 14 '16 at 15:58
  • @EricD Yes I'm sure! I'll check again and see what happens. Will update if I found something. Thanks mate. – Raffi Jun 14 '16 at 16:05
  • 2
    I'm having the same issue. There is no reason for the downvotes. – Siamaster Jun 16 '16 at 13:59

5 Answers5

7

Try using Foundation.URLSession where ever you use URLSession.

Jorn van Dijk
  • 1,973
  • 1
  • 15
  • 23
2

/Got it to work/ In some cases try to copy your code somewhere else then remove everything in your class that uses URLSession then type the session methods again and put back your copied code you should be fine.

Raffi
  • 699
  • 1
  • 8
  • 19
1

Update your URLSessin functions with;

func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
    self.data.append(data as Data)  
}

func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
    if error != nil {
        print("Failed to download data")
    }else {
        print("Data downloaded")
        self.parseJSON()
    }
}
Ali
  • 323
  • 3
  • 15
0

I can explain how but by playing around with the code I got this to work in SWIFT 3 after two days of frustration. I guess SWIFT 3 removed a lot of unnecessary words.

let task = Foundation.URLSession.shared.dataTask(with: <#T##URL#>, completionHandler: <#T##(Data?, URLResponse?, Error?) -> Void#>)
0

Here's where I am right now. It's not perfect but works maybe half of the time.

First, in the class where my URLsession is defined:

import Foundation
class Central: NSObject, URLSessionDataDelegate, URLSessionDelegate, URLSessionTaskDelegate, URLSessionDownloadDelegate {

I don't think all of that is necessary, but there it is. Then here is the function that is called by my background fetch:

func getWebData() {
    var defaults: UserDefaults = UserDefaults.standard
    let backgroundConfigObject = URLSessionConfiguration.background(withIdentifier: "myBGconfig")
    let backgroundSession = URLSession(configuration: backgroundConfigObject, delegate: self, delegateQueue: nil)
    urlString = "https://www.powersmartpricing.org/psp/servlet?type=dayslider"
    if let url = URL(string: urlString) {
    let rateTask = backgroundSession.downloadTask(with: URL(string: urlString)!)
    rateTask.taskDescription = "rate"
    rateTask.resume()
}

When the task comes back:

func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL ) {
    if downloadTask.taskDescription == "rate" {  // I run 2 web tasks during the session
        if let data = NSData(contentsOf: location) {
           var return1 = String(data: data as! Data, encoding: String.Encoding.utf8)!
             DispatchQueue.global(qos: .userInteractive).asyncAfter(deadline: .now() + 0.2){
                var defaults: UserDefaults = UserDefaults.standard
                defaults.set(myNumber, forKey: "electricRate") // myNumber is an extract of the text in returned web data
                defaults.set(Date(), forKey: "rateUpdate")
                defaults.synchronize()
                self.calcSetting()  //Calls another function defined in the same class.  That function sends the user a notification.
                let notificationName = Notification.Name("GotWebData")
                NotificationCenter.default.post(name: notificationName, object: nil)
            }   //  Closes the Dispatch
        }
      if session.configuration.identifier == "myBGconfig" {
          print("about to invalidate the session")
          session.invalidateAndCancel()
       }
}

I haven't figured out yet how to kill the session when BOTH tasks have completed, so right now I kill it when either one is complete, with invalidateAndCancel as above.

And finally, to catch errors:

func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didCompleteWithError: Error?) {
      if downloadTask.taskDescription == "rate" {
        print("rate download failed with error \(didCompleteWithError)")
    }
    if downloadTask.taskDescription == "other" {
        print("other download failed with error \(didCompleteWithError)")
    }
 downloadTask.resume()  //  I'm hoping this retries if a task fails?
}

func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
    if let error = error as? NSError {
        print("invalidate, error %@ / %d", error.domain, error.code)
    } else {
        print("invalidate, no error")
    }
}
Wayne Henderson
  • 237
  • 3
  • 9
  • I can see why your task is not always working when you create a background session set its isDiscretionary property to false and your task will always work immediately. Read here [isDiscretionary referance](https://developer.apple.com/reference/foundation/urlsessionconfiguration/1411552-isdiscretionary) and for cancelling sessions read answer here [Cancelling NSURLSession](http://stackoverflow.com/questions/21414823/how-do-you-know-when-the-nsurlsession-object-has-been-invalidated-by-ios) – Raffi Nov 11 '16 at 15:19
  • That reference says the default value of isDiscretionary is false. I'll try setting anyway, but it shouldn't matter? For cancelling a session, I'm getting close. I'm using a flag but there was a timing issue because my flag is set during a dispatch. The decision to invalidate is being made too quickly, before the task flag is set to "Done". Working on it. – Wayne Henderson Nov 11 '16 at 16:48
  • Try it and tell me what happens. For some reason it works for me this way. – Raffi Nov 11 '16 at 16:57
  • I've gone through 2 background fetches so far. One worked, the other did not. Ooh, another just finished successfully, so that's 2 of 3. I send a notification to the screen when every background fetch is called. Then each of my two tasks sends a notification if they finish. So I see a failure as only the fetch notification without both task notifications also arriving. Success is all 3 arriving. My task flags are working properly now and I can invalidate the session when both tasks finish. – Wayne Henderson Nov 11 '16 at 17:40
  • It's been going several hours now and seems to be working ~80% of the time. It's frustrating that it's not 100% but it's much better than it was. I believe setting the isDiscretionary to false did help. I wish there was a better way to debug whatever is going on in the background. – Wayne Henderson Nov 11 '16 at 20:41
  • If you are some how starting the background session in the background then isDiscretionary is always true no matter what you set it to. If that is the case then you should not worry the system will handle it. – Raffi Nov 11 '16 at 21:11