Swift HTTP Request Completion Block not Working Properly - swift

I have a weird problem with HTTP requests in Swift. I have the following simple code:
let session = URLSession.shared
let url = URL(string: "https://jsonplaceholder.typicode.com/posts")!
let task = session.dataTask(with: url, completionHandler: { data, response, error in
// Check the response
print(error)
print(response)
})
task.resume()
Running this code in a Xcode playground outputs the print statements. However, executing it in a standard Xcode project does not give any output.
I wonder if I am missing something in my code or if something is wrong with my Xcode setup.
Thanks in advance!
Edit:
Here's a screenshot of Xcode

As Tarun indicated, your executable is exiting before your call can be made. One way you can wait before exiting is to use a DispatchGroup like the following.
let session = URLSession.shared
let url = URL(string: "https://jsonplaceholder.typicode.com/posts")!
let group = DispatchGroup()
group.enter()
let task = session.dataTask(with: url, completionHandler: { data, response, error in
// Check the response
print(error)
print(response)
group.leave()
})
task.resume()
group.wait()

Looking at this screenshot -
It doesn't seem like any part of your code is executing at all. There's no entry point defined.
You can try following -
#main
struct NewApp {
static func main() {
// Paste all of your code here
// Put breakpoints and inspect what you want
}
}

Related

Networking in swift outside a playground or project?

I was trying to build a program that I would regularly run to check if a website has changed. It's working in Swift Playgrounds, however, if I try to just copy it over to a .swift file and to then run it directly in the terminal, it doesn't return a response. In fact, it doesn't seem to be doing anything.
Here's my code:
func getSite(website: String) {
let url = URL(string: website)!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error?.localizedDescription)
} else {
if let data = data, let host = response?.url?.host, let scheme = response?.url?.scheme {
print("Retrieved data from \(host) over \(scheme)...")
let site = String(data: data, encoding: .utf8)
print(site)
}
}
}
task.resume()
}
Any ideas?
Thanks!
Ok, I just figured it out - Alexander's comment was a huge help.
I just needed to set up a semaphore:
var semaphore = DispatchSemaphore(value: 0)
and semaphore.wait() right after running the async thread. At the end of the completion handler, I put semaphore.signal(). That's it!

Use Swift URLSession example code on command line tool

I am trying to figure out the simplest way to make an HTTP request in Swift 4 from the command line. I have copied this code from the URLSession programming guide, and added a couple print statements. I can't figure out why the .dataTask is not executing.
print("Testing URLSession")
let sessionWithoutADelegate = URLSession(configuration: URLSessionConfiguration.default)
if let url = URL(string: "https://www.example.com/") {
print("Encoded url \(url)")
(sessionWithoutADelegate.dataTask(with: url) { (data, response, error) in
print("Executing dataTask")
if let error = error {
print("Error: \(error)")
} else if let response = response,
let data = data,
let string = String(data: data, encoding: .utf8) {
print("Response: \(response)")
print("DATA:\n\(string)\nEND DATA\n")
}
}).resume()
}
The objective is to retrieve data from a REST api, but I can't even make a simple GET request to work properly...
I finally figured out how to make it work using CFRunLoop:
let runLoop = CFRunLoopGetCurrent()
let task = session.dataTask(with: request) { (data, response, error) in
print("Retrieved data")
CFRunLoopStop(runLoop)
}
task.resume()
CFRunLoopRun()
print("done")

How can we wait for HTTP requests to finish?

Using several answers on SO, we have managed to write and execute a basic HTTP request:
import Foundation
let url:URL = URL(string: "http://jsonplaceholder.typicode.com/posts")!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
let paramString = "data=Hello"
request.httpBody = paramString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest) {
(data, response, error) in
guard let data = data, let _:URLResponse = response, error == nil else {
print("error")
return
}
let dataString: String = String(data: data, encoding: String.Encoding.utf8)!
print("here")
print("Data: \(dataString)")
print("Response: \(response!)")
}
task.resume()
while task.response == nil {}
print("Done")
You'll note that we already busy-wait until task.response is set. However, neither data nor response are printed, only here.
After endless trials with wrapping things this or that way we determine that we have a Heisenbug here: changing nothing in the code, sometimes here is printed, sometimes nothing, and very, very rarely dataString (let alone response).
So we insert sleep(3) before print("Done") and, wonder of wonders, we get all prints.
Then we yelled a little bit (I may actually have thrown something), thought briefly about abandoning Swift altogether, but then calmed down enough to facepalm like sirs and post here.
Apparently, the main thread terminates whether or not any asynchronous tasks (threads?) are still running or not, killing all its spawn. How can we prevent that from happening, that is "join" the threads?
Bonus question: Does Alamofire deal with this behind the covers?
Using CwUtils by Matt Gallagher, I implemented a simple CountdownLatch which does the job:
import Foundation
import CwlUtils
<...>
let task = session.dataTask(with: request as URLRequest) {
(data, response, error) in
<...>
latch.countDown()
}
task.resume()
latch.await()
The most straight-forward (and built-in) way is probably to use a DispatchSemaphore:
<...>
let sem = DispatchSemaphore(value: 0)
let task = session.dataTask(with: request as URLRequest) {
(data, response, error) in
<...>
sem.signal()
}
task.resume()
sem.wait()
Active waiting seems to be the only way on the GCD. Using standard library material, this is what works:
import Foundation
<...>
var done = false
let task = session.dataTask(with: request as URLRequest) {
(data, response, error) in
<...>
done = true
}
task.resume()
repeat {
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.1))
} while !done

How do you test a URL and get a status code in Swift 3?

I'm using the most recent version of Xcode (8.1 at time of writing), which uses Swift 3.0.
All I'm trying to do is take a string, convert it to a URL and test that URL to see if it gives me a 404 error. I've been able to make a URL and URLRequest by using:
let url = URL(string: fullURL)
let request = URLRequest(url: url!)
but I've found myself unable to get anything working beyond that. I've searched around for help, but most, if not all of it, is written in Swift 2.0, which I've tried to convert to no avail. It seems that even if you change the naming convention to remove the NS prefix, that isn't enough. I tried using:
let response: AutoreleasingUnsafeMutablePointer<URLRequest> = nil
but that gives me an error that "fix-it" makes worse by sticking question marks and semi-colons everywhere.
Apple's documentation isn't helping me much, either. I'm seriously at a loss.
Does anybody know how to correctly set up and test a URL for 404 status in Swift 3.0?
try this out to give you the status codes of the responses - 200, 404 etc:
let url = URL(string: fullURL)
let task = URLSession.shared.dataTask(with: url!) { _, response, _ in
if let httpResponse = response as? HTTPURLResponse {
print(httpResponse.statusCode)
}
}
task.resume()
You could also do the same, simply replacing the with: url! to use the request var as you defined in your example e.g. let task = URLSession.shared.dataTask(with: request) {...} But in this example I don't think you need to really.
Simple example:
let url = // whatever
let session = URLSession.shared
let task = session.downloadTask(with:url) { loc, resp, err in
let status = (resp as! HTTPURLResponse).statusCode
print("response status: \(status)")
}
task.resume()
Here is one more example from delegate method
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didBecome downloadTask: URLSessionDownloadTask){
let responseStatusCode = (dataTask.response as! HTTPURLResponse).statusCode
}
Here is the example https://github.com/ankitthakur/SwiftNetwork/blob/master/Sources/Shared/SwiftNetwork.swift

datataskwithURL() completion block not being called. Xcode swift

I am trying to write a basic function to make an HTTP Get request and parse the xml data that comes back. I already have completed the section for parsing XML from a local file, but i can't seem to get any data from the server. I tested this code below, but the completion block does not even run, for information to be passed back from the server. Any suggestions please.
func getDatafromURL (url: String) {
guard let urlforRequest = NSURL(string: url) else {
print("Error: cannot create URL")
return
}
let request = NSURLRequest(URL: urlforRequest)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
print(response)
print(error)
}
task.resume()
}