Swift 3: URLSession / URLRequest Not Working
Asked Answered
M

1

8

I am still trying to convert our application from Swift 2 over to Swift 3 because I am being forced to since all of our Apple devices are now running iOS 10.

I have gone through the code conversion and thought I was doing well however, while attempting to debug my JSON issues (posted in another question), I am now dealing with requests not even being sent.

let params: [String:AnyObject] = [
    "email":"\(self.preferences.string(forKey: "preference_email")!)" as AnyObject
]
let requestParams: [String:AnyObject] = [
    "action":"601" as AnyObject,
    "params":params as AnyObject
]

do {
    let requestObject = try JSONSerialization.data(withJSONObject: requestParams, options:[])
    var request = URLRequest(url: URL(string: "http://domain.tld/path/")!)

    request.httpBody = requestObject
    request.httpMethod = "POST"

    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)

    NSLog("Got here?")

    session.dataTask(with: request) {data, response, error in
        guard let data = data, error == nil else {
            print("error=\(error)")
            return
        }

        NSLog("Got here 3?")

        let object:JSON = JSON(data:data)

        NSLog("Object: \(object)")
    }.resume()

    NSLog("Got here 4?")
} catch {
    NSLog("Got here catch?")
}

NSLog("End of getUser")

The code above yields the following output:

2016-10-04 13:00:12.011969 OneTouch[1589:623015] [DYMTLInitPlatform] platform initialization successful
2016-10-04 13:00:12.264319 OneTouch[1589:622954] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles
2016-10-04 13:00:12.265321 OneTouch[1589:622954] [MC] Reading from public effective user settings.
2016-10-04 13:00:12.295055 OneTouch[1589:622954] Got here?
2016-10-04 13:00:12.295445 OneTouch[1589:622954] Got here 4?
2016-10-04 13:00:12.295515 OneTouch[1589:622954] End of getUser
(lldb) 

Which means that the request isn't even being made. Is there some key that I have to add to the PLIST again? This is starting to get annoying.

Below is my old code and it isn't even working anymore:

let params: [String:AnyObject] = [
    "email":"\(self.preferences.string(forKey: "preference_email")!)" as AnyObject
]
let requestParams: [String:AnyObject] = [
    "action":"601" as AnyObject,
    "params":params as AnyObject
]

do {
    let requestObject = try JSONSerialization.data(withJSONObject: requestParams, options:[])
    let request = NSMutableURLRequest(url: URL(string: "http://domain.tld/path/" as String)!, cachePolicy:NSURLRequest.CachePolicy.reloadIgnoringLocalCacheData, timeoutInterval: 20)

    request.httpBody = requestObject
    request.httpMethod = "POST"

    NSLog("Got here?")

    let task = URLSession.shared.dataTask(with: request as URLRequest, completionHandler: {data, response, error in
        if error != nil {
            NSLog("Got here 2?")
        }

        NSLog("Got here 3?")

        let object:JSON = JSON(data:data!)

        NSLog("Object: \(object)")
    })

    NSLog("Got here 4?")

    task.resume()
} catch {
    NSLog("Got here catch?")
}

NSLog("End of getUser")

The code above yields the same output as the other code does!

Modiolus answered 4/10, 2016 at 20:6 Comment(10)
"trying to convert our application from Swift 2 over to Swift 3 because I am being forced to since all of our Apple devices are now running iOS 10" ... You don't have to convert to Swift 3. You could use Swift 2.3.Eaglet
I tried to Convert to 2.3 however, some of the modules that I am using weren't working so I updated to Swift 3 and now I have the issues cleared up with the modules, or so I hope.Modiolus
Where is the breakpoint you are setting when you are looking at the log output? Are you sure you aren't just pausing it before the network call has had time to complete and call the completion handler?Pastis
So the code above is in a function called getUser() which is called when the application is launched. The breakpoint is on the next line after the getUser() function is called therefore, it should be processing the entire function. Also, it logs "End of getUser" which as at the end of the function so I know the entire function is being processed.Modiolus
At least declare your param dictionaries as [String:Any] and remove the as AnyObject casts and use native URLRequest rather than NSURLRequestDowzall
vadian: Okay, I changed the parameters. Thank you for the hint. That was my poor excuse to get it to work after converting to Swift 3. Thanks to you I have defined how I wanted it.Modiolus
Rob: Okay, so what changed to cause that because before I converted to Swift 3, I could do the exact same thing it was working without a problem. I was able to print the data returned immediately.Modiolus
Rob: you are correct, thank you. I removed the break point and do see the response back. This makes it difficult for me to debug because of the way I am used to debugging code. Is there anyway to get the app to halt and wait for a response?Modiolus
You can, theoretically, use semaphores to make it wait, but that's a horrible pattern, even if just debugging. Put your breakpoint inside the completion handler if you want it to stop when the response comes in.Eaglet
I tried putting it inside the completionHandler however, the data wasn't there. I supposed I can try it again, but it didn't work before. Thank you!Modiolus
E
0

If you put a breakpoint immediately after calling getUser, the URLSession task's completion handler, which runs asynchronously (i.e. generally finishes later, unless the request failed immediately or was satisfied by some cached response) may not have had a chance to be called.

If you put a breakpoint inside the dataTask completion handler, you should see your data at that point.


Personally, I'd make sure to give getUser a completion handler so you know when it's done:

func getUser(completionHandler: @escaping (JSON?, Error?) -> Void) {
    let params = [
        "email":"\(preferences.string(forKey: "preference_email")!)"
    ]

    let requestParams: [String: Any] = [
        "action": "601",
        "params": params
    ]

    do {
        let requestObject = try JSONSerialization.data(withJSONObject: requestParams)

        var request = URLRequest(url: URL(string: "http://domain.tld/path/")!, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 20)

        request.httpBody = requestObject
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.setValue("application/json", forHTTPHeaderField: "Accept")

        let task = URLSession.shared.dataTask(with: request) {data, response, error in
            guard let data = data, error == nil else {
                completionHandler(nil, error)
                return
            }

            completionHandler(JSON(data: data), nil)
        }

        task.resume()
    } catch {
        completionHandler(nil, error)
    }
}

Then when you call it, you can do something like:

getUser { json, error in
    guard let json = json else {
        print(error)
        return
    }

    // do something with json
    print(json)
}

And just put your breakpoint in getUser's completion handler. And remember that you have no assurances that the completion handler will run on the main queue or not, so you'll want to make sure to dispatch and UI or model updates back to the main queue.

Eaglet answered 4/10, 2016 at 20:37 Comment(4)
I really like this approach and will probably go back and implement this later, but not at this time. I did get this working thank you to another response, but this answer seems more appropriate! Thank you.Modiolus
@Modiolus Could you please document what solved this for you? Struggling with the same problemRosalbarosalee
The problem for me was the result was taking longer to come back than I was allowing. So, while the result was coming back, I was attempting to break and print the result before the result was actually returned.Modiolus
I'm not seeing where params constant is actually used. What was the purpose of this constant?Chadwell

© 2022 - 2024 — McMap. All rights reserved.