Why my return is nil but if i press the url in chrome/safari, i can get data? - swift

#IBAction func mainButtonnBeTapped(sender: AnyObject) {
let session = NSURLSession.sharedSession()
let request = NSURLRequest(URL: NSURL(string: "http://hq.sinajs.cn/list=sz000609")!)
let task = session.dataTaskWithRequest(request, completionHandler: {
(data, response, error) -> Void in
let myString = NSString(data: data, encoding: NSUTF8StringEncoding)
println("this is my string: \(myString)")
})
task.resume()
}
I am using above url to try to get some data, but the return is nil, but i enter the url in chrome/safari, i can get some data.
I really don't why, can anyone help to explain?

This HTTP server sends a
Content-Type = application/x-javascript; charset=GBK
header field in the response, therefore you get the correct encoding from the textEncodingName property of the NSURLResponse. This can be
converted to a NSStringEncoding.
This is just a translation of the solution presented in https://stackoverflow.com/a/19885463/1187415 to Swift, plus some
simple error checking:
let session = NSURLSession.sharedSession()
let request = NSURLRequest(URL: NSURL(string: "http://hq.sinajs.cn/list=sz000609")!)
let task = session.dataTaskWithRequest(request, completionHandler: {
(data, response, error) -> Void in
var usedEncoding = NSUTF8StringEncoding // Some fallback value
if let encodingName = response.textEncodingName {
let encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding(encodingName))
if encoding != UInt(kCFStringEncodingInvalidId) {
usedEncoding = encoding
}
}
if let myString = NSString(data: data, encoding: usedEncoding) {
println("this is my string: \(myString)")
} else {
println("failed to decode data")
}
})
task.resume()
Output:
this is my string: var hq_str_sz000609="绵世股份, ....
Minor changes are necessary for Swift 2:
let session = NSURLSession.sharedSession()
let request = NSURLRequest(URL: NSURL(string: "http://hq.sinajs.cn/list=sz000609")!)
let task = session.dataTaskWithRequest(request, completionHandler: {
(data, response, error) -> Void in
var usedEncoding = NSUTF8StringEncoding // Some fallback value
if let encodingName = response?.textEncodingName {
let encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding(encodingName))
if encoding != UInt(kCFStringEncodingInvalidId) {
usedEncoding = encoding
}
}
if let myString = String(data: data!, encoding: usedEncoding) {
print("this is my string: \(myString)")
} else {
print("failed to decode data")
}
})
task.resume()
Update for Swift 3:
let session = URLSession.shared
let request = URLRequest(url: URL(string: "http://hq.sinajs.cn/list=sz000609")!)
let task = session.dataTask(with: request, completionHandler: {
(data, response, error) -> Void in
guard let data = data else { return }
var usedEncoding = String.Encoding.utf8 // Some fallback value
if let encodingName = response?.textEncodingName {
let encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding(encodingName as CFString))
if encoding != UInt(kCFStringEncodingInvalidId) {
usedEncoding = String.Encoding(rawValue: encoding)
}
}
if let myString = String(data: data, encoding: usedEncoding) {
print("this is my string: \(myString)")
} else {
print("failed to decode data")
}
})
task.resume()

The text you try to get is probably not UTF-8, try with another encoding, like this for example:
let myString = NSString(data: data, encoding: NSASCIIStringEncoding)
Update: read Martin R's answer for how to find the right encoding.

Related

Swift, how can I return the data from HTTP request?

I have found learning swift to be more or less unbearable to do anything, something that would be done in a single line in Python becomes a whole task in swift.
I am trying to return the data from a http request and cannot find a single source that explains how. The only things I can find prints the data instead of returning it, either as a dictionary (from using JSONSerialization) or simply as a string.
let url = URL(string: "url")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error {
print("error: \(error)")
} else {
if let response = response as? HTTPURLResponse {
print("statusCode: \(response.statusCode)")
}
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print("data: \(dataString)")
}
}
}
task.resume()
func makePostRequest(){
let urlPath: String = "http://www.swiftdeveloperblog.com/http-post-example- script/"
var url: NSURL = NSURL(string: urlPath)!
var request: NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
var stringPost="firstName=James&lastName=Bond" // Key and Value
let data = stringPost.dataUsingEncoding(NSUTF8StringEncoding)
request.timeoutInterval = 60
request.HTTPBody=data
request.HTTPShouldHandleCookies=false
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{ (response:NSURLResponse!, data: NSData!, error: NSError!) -> Void in
var error: AutoreleasingUnsafeMutablePointer<NSError?> = nil
let jsonResult: NSDictionary! = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers, error: error) as? NSDictionary
if (jsonResult != nil) {
// Success
println(jsonResult)
let message = jsonResult["Message"] as! NSString
println(message)
}else {
// Failed
println("Failed")
}
})
}

How to wait for a download task to finish in swift 3

I am trying to build a user regeistration form, which should check if the user already exists. So I am sending a php request to my my mysql server. If the return value is empty, the user does not exists yet.
Unfortunatley I am really stuck with waiting for this check to finish. I tried several solutions I found googleing but none of them worked. My current code uses semaphores and will crash with "fatal error: unexpectedly found nil while unwrapping an Optional value", so the semaphore is not waiting until the task is finished as I would expect it.
Any hints, would be greatly appreciated. Thanks guys.
private func isUniqueEmail(email: String) -> Bool {
var result: Bool?
let semaphore = DispatchSemaphore(value: 1)
let requestURL = URL(string: "http://localhost/firstpostget/functions/get.php")
var request = URLRequest(url: requestURL!)
request.httpMethod = "POST"
let postParameters = "email=" + email
request.httpBody = postParameters.data(using: .utf8)
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) {(data, response, error) in
var myJson: AnyObject
do{
myJson = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if myJson.count == 0{
result = true
semaphore.signal()
} else{
result = false
semaphore.signal()
}
} catch{
//TODO
print(error)
}
}
task.resume()
semaphore.wait(timeout: .distantFuture)
return result!
}
Your task is async and you are force unwrapping nil value so this is the reason it crashes.
You have to change your function implementation to also be async, for example using closures:
private func isUniqueEmail(email: String, completion: ((Bool) -> (Void))?) {
let requestURL = URL(string: "http://localhost/firstpostget/functions/get.php")
var request = URLRequest(url: requestURL!)
request.httpMethod = "POST"
let postParameters = "email=" + email
request.httpBody = postParameters.data(using: .utf8)
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) {(data, response, error) in
var myJson: AnyObject
do{
myJson = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if myJson.count == 0 {
completion?(true)
} else{
completion?(false)
}
} catch{
//TODO
print(error)
}
}
task.resume()
}
Now you can use this function in this way:
isUniqueEmail(email: "aaa#bbbb.com") { result in
if result {
print("email unique")
} else {
print("email not unique")
}
}
I think you should rethink the pattern you're using to get the data out of your request, you should consider using a custom handler/callback method that you pass along with the email you're trying to check. See below for an example:
private func isUniqueEmail(email: String, handler: ((_ result: Bool) -> Void)?) -> Void {
let requestURL = URL(string: "http://localhost/firstpostget/functions/get.php")
var request = URLRequest(url: requestURL!)
request.httpMethod = "POST"
let postParameters = "email=" + email
request.httpBody = postParameters.data(using: .utf8)
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) {(data, response, error) in
var myJson: AnyObject
var result: Bool = false
do{
myJson = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if myJson.count == 0 {
result = true
}
guard handler != nil else {
return
}
handler!(result)
} catch{
//TODO
print(error)
}
}
task.resume()
}
Run:
isUniqueEmail(email: "test#test.com", handler: { result in
print(result) // true || false
})
If you really want to go down the "wait" route then take a took at DispatchGroup's
https://developer.apple.com/documentation/dispatch/dispatchgroup
try using this:
ApihelperClass
static let sharedInstance = ApihelperClass()
typealias CompletionHandler = (_ success:Bool, _ error:Bool, _ result:NSDictionary) -> Void
typealias ErrorHandler = (_ success: Bool, _ error:Bool) -> Void
func callPostRequest(_ urlPath: String, params:[String: AnyObject], completionHandler: #escaping CompletionHandler, errorHandler:#escaping ErrorHandler ){
print("urlPath:==> \(urlPath) ")
let session = Foundation.URLSession.shared
let url = URL(string: urlPath)
var request = URLRequest(url : url!)
request.httpMethod = "POST"
do {
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.httpBody = jsonData
session.dataTask(with: request, completionHandler: { data, response, error in
OperationQueue.main.addOperation {
guard error == nil && data != nil else { // check for fundamental networking error
print("error=\(error)")
errorHandler(false, true)
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
// print("response = \(response)")
}
let responseString = String(data: data!, encoding: String.Encoding.utf8)
print("responseString = \(responseString!)")
if let responsedata = responseString!.data(using: String.Encoding.utf8)! as? Data{
do {
let jsonResult:NSDictionary = try JSONSerialization.jsonObject(with: responsedata, options: []) as! NSDictionary
print("Get The Result \(jsonResult)")
//parse your jsonResult as per your requirements
if error != nil {
print("error=\(error)")
completionHandler(false, true, jsonResult)//
}
if let str = jsonResult["success"] as? NSNull {
print("error=\(str)")
completionHandler(false, true, jsonResult)
}
else {
let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
// print("Response string : \(responseString)")
completionHandler(true, false, jsonResult)
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
}
}) .resume()
}catch {
print("Error ->Catch")
}
}
Add to your viewcontroller
func isUniqueEmail(email: String){
ApihelperClass.sharedInstance.callPostRequest("http://localhost/firstpostget/functions/get.php", params: ["email":email as AnyObject], completionHandler: { (success, error, result) in
//success 200
}) { (success, error) in
//error
}
}
you can use URlSession like :
func isUniqueEmail(email: String,completion: #escaping (Bool) -> ()) {
var request = URLRequest(url: URL(string: "http://localhost/firstpostget/functions/get.php")!)
request.httpMethod = "POST"
let postString = "email=\(email)"
request.httpBody = postString.data(using: .utf8)
// loading to wait request
UIApplication.shared.isNetworkActivityIndicatorVisible = true
let task = URLSession.shared.dataTask(with: request) { data, response, error in
// we get request request
UIApplication.shared.isNetworkActivityIndicatorVisible = false
guard let data = data, error == nil else { // check for fundamental networking error
print("error=\(String(describing: error))")
completion(false)
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(String(describing: response))")
completion(false)
}else{
completion(true)
}
let responseString = String(data: data, encoding: .utf8)
print("responseString = \(String(describing: responseString))")
}
task.resume()
}
and used in code Like
self.isUniqueEmail(email: "your Email") { (isExit) in
if isExit {
}else{
}
}
Ok, I just found a solution. My semaphore approach actually worked as well as dispatchgroups. The task just needed to be URLSession.shared.dataTask
Still thank's a lot for all the answers.

Appending a variable outside the scope of a function in Swift 3

I am trying to append jsonFile, however, I am not able to append jsonFile with a new NSString unless I use a mutuable variable for the parameter file in the function. I have found the solution of using "inout" but in this case I get the error of "scaping closures can only capture inout parameters explicitly by value file.append(jsonData)
var jsonFile: [NSString] = []
func function(file: inout [NSString]){
let request = NSMutableURLRequest(url: URL(string: "https://parse.udacity.com/parse/classes/StudentLocation?limit=1")!)
request.addValue("QrX47CA9cyuGewLdsL7o5Eb8iug6Em8ye0dnAbIr", forHTTPHeaderField: "X-Parse-Application-Id")
request.addValue("QuWThTdiRmTux3YaDseUSEpUKo7aBYM737yKd4gY", forHTTPHeaderField: "X-Parse-REST-API-Key")
let session = URLSession.shared
let task = session.dataTask(with: request as URLRequest) { data, response, error in
if error != nil { // Handle error...
return
}
let jsonData = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)!
print(NSString(data: data!, encoding: String.Encoding.utf8.rawValue)!)
file.append(jsonData)
}
task.resume()
}
function(file: &jsonFile)
print(jsonFile)
You are dealing with asynchronous code. You should not use an inout parameter. Instead, you should be using a completion handler.
Something like the following will work:
var jsonFile: [String] = []
func function(completion: #escaping (String?) -> Void) {
var request = URLRequest(url: URL(string: "https://parse.udacity.com/parse/classes/StudentLocation?limit=1")!)
request.addValue("QrX47CA9cyuGewLdsL7o5Eb8iug6Em8ye0dnAbIr", forHTTPHeaderField: "X-Parse-Application-Id")
request.addValue("QuWThTdiRmTux3YaDseUSEpUKo7aBYM737yKd4gY", forHTTPHeaderField: "X-Parse-REST-API-Key")
let session = URLSession.shared
let task = session.dataTask(with: request) { data, response, error in
if let data = data, error != nil {
if let jsonData = String(data: data, encoding: .utf8) {
completion(jsonData)
} else {
completion(nil)
}
} else {
completion(nil)
}
}
task.resume()
}
function() { (string) in
if let string = string {
jsonFile.append(string)
}
print(jsonFile)
}
Note all of the other clean up. Don't use NSString, use String. Don't use NSMutableURLRequest, use URLRequest with var.

Is it possible to return result set from POST call

I have this code:
private func data_request(url : String)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let url:NSURL = NSURL(string: self.self.newUrl.createUrl(url))!
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData
let paramString = "data=Hello"
request.HTTPBody = paramString.dataUsingEncoding(NSUTF8StringEncoding)
let task = session.dataTaskWithRequest(request) {
(
let data, let response, let error) in
guard let _:NSData = data, let _:NSURLResponse = response where error == nil else {
print("error")
return
}
let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding)
}
task.resume()
}
}
I call it like this:
var result = data_request("localhost/test");
That is working fine, but is it possible to return the results from the request function? My plan is to put the result in the result variable.
You should use closures to get the data since the NSURLSession API is asynchronous, meaning that you don't know when the data will arrive. It may be instantly or in 10 seconds, you never know. You will return from the function immediately, but you'll get the value from the closure.
private func data_request(url : String, completion: (String) -> ()) {
//...
//...
let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding)
// Now call completion to pass the value
completion(dataString)
}
And when you need to call the function you will use:
data_request("http://someapi.com/api") {
dataString in
print(dataString) // This is the string you passed to the completion
}

how to make a HTTP request in Swift

Code
let url = NSURL(string:"http://www.stackoverflow.com")
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) { (data, response, error ) in
if error == nil {
var urlContent = NSString(data: data, encoding: NSUTF8StringEncoding)
println(urlContent)
}
}
Try like this:
let url = NSURL(string:"http://www.stackoverflow.com")!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in
dispatch_async(dispatch_get_main_queue()) {
if let data = data {
if let urlContent = NSString(data: data, encoding: NSUTF8StringEncoding) {
println(urlContent)
}
} else if let error = error {
println(error.description)
}
}
//
}
task.resume()
See the Apple docs for dataTaskWithURL(_:completionHandler:).
After you create the task, you must start it by calling its resume method.