Swift: Return JSON string from NSURLSession task - json

I'd like to get token as String type, however the following code returns error as " "String" is not convertible to "Void" ".
Could you tell me what is the problem?
In order to parse JSON, I used SwiftyJSON
func authentication() -> String {
let request = NSMutableURLRequest(URL: NSURL(string: "https://~~~/v2/authenticate/api")!)
request.HTTPMethod = "POST"
var loginID = "my_ID"
var apiKey = "my_APIKEY"
var postString:NSString = "login_id=\(loginID)&api_key=\(apiKey)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
println("error=\(error)")
return
}
println("response = \(response)")
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)
println("responseString = \(responseString)")
let json = JSON(data:data)
if let token = json["auth_token"].stringValue{
println("\(token)")
return token ///Error Here
}
}
task.resume()
}
※Edited Jan6th 7:50(GMP+9hr)
I edited as following, however I got error as ""Use of unresolved identifier "tokenString".
Please tell me how to solve the problem.
func authentication() -> String {
let request = NSMutableURLRequest(URL: NSURL(string: "https://~~~/v2/authenticate/api")!)
request.HTTPMethod = "POST"
var loginID = "my_ID"
var apiKey = "my_APIKEY"
var postString:NSString = "login_id=\(loginID)&api_key=\(apiKey)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
println("error=\(error)")
return
}
println("response = \(response)")
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)
println("responseString = \(responseString)")
let json = JSON(data:data)
if let token = json["auth_token"].stringValue{
println("\(token)")
tokenString = self.didReceiveAuthToken(token)///Edited
}
}
task.resume()
return tokenString
}
func didReceiveAuthToken(token : String) ->String{
return token
}

The dataTaskWithRequest closure takes a return type of void. Your method that returns a string ends immediately after task.resume() is executed and does not return anything.
Your basic problem is that you're applying synchronous thinking to an asynchronous task. One easy suggestion is to have authentication() return void (ie, nothing), and then make a separate method like didReceiveAuthToken(token : String) that is called by your completion handler when the token is received.

Related

How else can I format the data? swift parse json - The data couldn’t be read because it isn’t in the correct format

The JSON data is
[
{"id":0,"temperature":77,"humidity":0.22,"timeCaptured":"2020-09-25T19:33:27.9733333"},
{"id":0,"temperature":77,"humidity":0.22,"timeCaptured":"2020-09-25T20:38:53.3"},
{"id":0,"temperature":85,"humidity":0.25,"timeCaptured":"2020-09-25T20:38:53.3"},
{"id":0,"temperature":88,"humidity":0.22,"timeCaptured":"2020-09-28T15:30:00"},
// ...
]
My structs look like this
struct TemperatureDataModel: Codable{
let id: Int?
let temperature: Double?
let humidty: Double?
let timeCaptured: String?
}
My function looks like this
func getTemperData(){
//Create the URLs
let temperatureDataUrl = URL(string: "https://weatherstationapi.azurewebsites.net/api/TemperatureSensor/GetData")
// let WindDataUrl = URL(string: "https://weatherstationapi.azurewebsites.net/api/WindData/GetAllData")
guard let requestURLTemp = temperatureDataUrl else { fatalError() }
//Create URL request
var request = URLRequest(url: requestURLTemp)
//Specifiy HTTP Method to use
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiaWxpci50YWlyaUB0dHUuZWR1IiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbmFtZWlkZW50aWZpZXIiOiI4MjEzYzhhMy1iODgxLTQ4NmUtOGUyMC1mZmNlMDlmNGY0ZjgiLCJuYmYiOiIxNjAyNTI2NDI1IiwiZXhwIjoiMTYwNTExODQyNSJ9.t1qnYyXLpRRJ3YQfhgLrylBqL_pdKOnKVMgOfG9IuVc", forHTTPHeaderField: "Authorization")
//Send HTTP Request
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else {return}
print(data)
//Use parseJSON to convert data
let TemperatureData = parseJSON(data: data)
// for singleValue in TemperatureData {
// print(singleValue.temperautre)
// }
//read list
guard let TemperatureDataModel = TemperatureData else {return}
print("Temperature is : \(TemperatureDataModel.temperature)")
// Check if error took place
if let error = error {
print("Error took place \(error)")
return
}
//Read HTTP Response Status Code
// if let data = data, let dataString = String(data: data, encoding: .utf8) {
// print("Response data string:\n \(dataString)")
// }
}
task.resume()
}
and then my JSON decoder function looks like this
func parseJSON(data: Data) -> TemperatureDataModel? {
var returnValue: TemperatureDataModel?
do {
let returnValue = try JSONDecoder().decode([TemperatureDataModel].self, from: data)
} catch {
print("Error took place \(error).")
}
return returnValue
}
I've looked at 6+ stack overflow posts now and still cannot figure It out. Ive tried putting my model in [] for an array, moving where the function is called, changing the jsondecoder function and more and nothing works.
I think you have to give a format to the date before you parse the data
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
Your issue there is that you are creating another returnValue that is not being returned. You need also change the return type to [TemperatureDataModel]
func parseJSON(data: Data) -> [TemperatureDataModel]? {
do {
return try JSONDecoder().decode([TemperatureDataModel].self, from: data)
} catch {
print("Error took place \(error).")
return nil
}
}

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (MSGraphDateTimeTimeZone)'

I am sending a request to create event API of MS Graph. Below is my function. When I execute it, I get 'Invalid type in JSON write (MSGraphDateTimeTimeZone)' error or 'Invalid type in JSON write (MSGraphAttendee)'.
I am using their Objective-C example of the request, so not sure what I am doing wrong - https://learn.microsoft.com/en-us/graph/api/group-post-events?view=graph-rest-1.0&tabs=objc
public func reserveHotel(email: String, startDateTime: Date, endDateTime: Date, locationName: String, bodyText: String? = nil) -> Promise<Data> {
let request = makeRequest(relativeUrl: "/me/calendar/events")
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("outlook.timezone=\"UTC\"", forHTTPHeaderField: "Prefer")
let event = MSGraphEvent()
event.subject = "Test"
event.isOrganizer = true
event.start = MSGraphDateTimeTimeZone.fromDate(startDateTime)
event.end = MSGraphDateTimeTimeZone.fromDate(endDateTime)
if let eventText = bodyText {
let body = MSGraphItemBody()
body.contentType = MSGraphBodyType.html()
body.content = eventText
event.body = body
}
let location = MSGraphLocation()
location.displayName = locationName
event.location = location
let attendee = MSGraphAttendee()
let emailMS = MSGraphEmailAddress()
emailMS.address = email
emailMS.name = locationName
attendee.emailAddress = emailMS
attendee.type = MSGraphAttendeeType.required()
event.attendees = [attendee]
do {
request.httpBody = try event.getSerializedData()
} catch {
log.error("Failed to create JSON request body for Microsoft Graph: \(error)")
return Promise(error: error)
}
return processRequest(request)
}
private func processRequest(_ request: NSMutableURLRequest) -> Promise<Data> {
return Promise { seal in
let dataTask = MSURLSessionDataTask(request: request, client: httpClient, completion: { (data: Data?, response: URLResponse?, error: Error?) in
// Check for server-side errors
if let response = response as? HTTPURLResponse {
let statusCode = response.statusCode
if (statusCode < 200) || (statusCode > 299) {
let description = "Microsoft Graph service returned HTTP response status \(statusCode) for URL \(request.url?.absoluteString ?? "")"
seal.reject(NSError(domain: NSURLErrorDomain, code: statusCode, userInfo: [NSLocalizedDescriptionKey: description]))
return
}
}
seal.resolve(data, error)
})
dataTask?.execute()
}
}

I need to parse this JSON response make with http request in swift

This is my script in php to have a Json response
<?php
include "dbconnect.php";
$id = $_POST['id'];
$uname = $_POST['uname'];
try {
$queryExistingBike = "SELECT StatoBici, Username FROM BICI INNER JOIN UTENTE ON utenteKEY = KEYutente WHERE (IdBike = '$id')";
$search = $connection->query($queryExistingBike);
//print_r ($search);
} catch (Exception $ex) {
echo $ex->getMessage();
}
$dati = $search->fetch_assoc();
if ($dati["Username"] == "$uname") {
$dati['Username'] = "true";
} else {
$dati['Username'] = "false";
}
//print_r($dati);
print json_encode($dati);
?>
This is the Json response from php script
{"StatoBici":"ok","Username":"false"}
This is the code in swift to make a http post request to web service
func jsonResponse(){
var request = URLRequest(url: URL(string: "http://bike1010.com/webservice/getBikeData.php")!)
request.httpMethod = "POST"
let postString = "id=2017&uname=admin" // Your parameter
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else { // check for fundamental networking error
print("error=\(String(describing: error))")
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))")
}
let responseString = try? JSONSerialization.jsonObject(with: data, options:[])
print(responseString)
}
task.resume()
}
The output into Xcode
Optional({
StatoBici : Ok
Username : Tue
})
if that's what you wanted to achieve:
let responseString = try? JSONSerialization.jsonObject(with: data, options:[]) as? [String:Any]
print(responseString)
//simple example for receiving each field from response
let statoBici:String = responseString["StatoBici"] as! String
let username:Bool = responseString["Username"] as! Bool
print("StatoBici received: \(statoBici), username received: \(username)")
As Wojcik mentioned in comment you already have parse JSON. Your responseString is actually parsed object. You can access values as:
if let obj = responseString as? NSDictionary {
let StatoBici = obj.value(for : "StatoBici") as! String
let okValue = obj.value(for : "OK") as! String // If OK is bool use bool
}

How to send a POST request through Swift?

I have my controller like this -
def create
if (#user = User.find_by_email(params[:email])) && #user.valid_password?(params[:password])
render json: #user.as_json(only: [:email,:authentication_token]),status: :created
else
render json:('Unauthorized Access')
end
end
When I use Postman to make this request, I choose Body, and form data and adds in the email and password. And this WORKS
How to use swift to do the same? This is what I have tried
let url = URL(string: "http://localhost:3000/api/v1/user_serialized/")
let config = URLSessionConfiguration.default
let request = NSMutableURLRequest(url: url!)
request.httpMethod = "POST"
let bodyData = "email=Test#test.com&password=Test1234"
request.httpBody = bodyData.data(using: String.Encoding.utf8);
let session = URLSession(configuration: config)
let task = session.dataTask(with: url! as URL, completionHandler: {(data, response, error) in
let json = JSON(data:data!)
debugPrint(json)
})
task.resume()
I have made a Custom HTTP class where we can sent url, parameter and we will get Data from API. Below is the class.
import Foundation
//HTTP Methods
enum HttpMethod : String {
case GET
case POST
case DELETE
case PUT
}
class HttpClientApi: NSObject{
//TODO: remove app transport security arbitary constant from info.plist file once we get API's
var request : URLRequest?
var session : URLSession?
static func instance() -> HttpClientApi{
return HttpClientApi()
}
func makeAPICall(url: String,params: Dictionary<String, Any>?, method: HttpMethod, success:#escaping ( Data? ,HTTPURLResponse? , NSError? ) -> Void, failure: #escaping ( Data? ,HTTPURLResponse? , NSError? )-> Void) {
request = URLRequest(url: URL(string: url)!)
logging.print("URL = \(url)")
if let params = params {
let jsonData = try? JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
request?.setValue("application/json", forHTTPHeaderField: "Content-Type")
request?.httpBody = jsonData//?.base64EncodedData()
//paramString.data(using: String.Encoding.utf8)
}
request?.httpMethod = method.rawValue
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 30
configuration.timeoutIntervalForResource = 30
session = URLSession(configuration: configuration)
//session?.configuration.timeoutIntervalForResource = 5
//session?.configuration.timeoutIntervalForRequest = 5
session?.dataTask(with: request! as URLRequest) { (data, response, error) -> Void in
if let data = data {
if let response = response as? HTTPURLResponse, 200...299 ~= response.statusCode {
success(data , response , error as? NSError)
} else {
failure(data , response as? HTTPURLResponse, error as? NSError)
}
}else {
failure(data , response as? HTTPURLResponse, error as? NSError)
}
}.resume()
}
}
Now you can refer below code to get how to make an API call.
var paramsDictionary = [String:Any]()
paramsDictionary["username"] = "BBB"
paramsDictionary["password"] = "refef"
HttpClientApi.instance().makeAPICall(url: "Your URL", params:paramsDictionary, method: .POST, success: { (data, response, error) in
// API call is Successfull
}, failure: { (data, response, error) in
// API call Failure
})
I think you should pass your request instead of the url to session.dataTask
here is how my code looks like:
private let url = URL(string: "http://example.com/")!
func httpPost(jsonData: Data) {
if !jsonData.isEmpty {
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = jsonData
URLSession.shared.getAllTasks { (openTasks: [URLSessionTask]) in
NSLog("open tasks: \(openTasks)")
}
let task = URLSession.shared.dataTask(with: request, completionHandler: { (responseData: Data?, response: URLResponse?, error: Error?) in
NSLog("\(response)")
})
task.resume()
}
}
Here is the Example of POST API for calling Login API with parameters "emailaddress" and "password" with userEmailID and Userpassword as two strings holding values for email and password respectively.
You can call this POST API anywhere in your view controller, as given below:
self.postLoginCall(url: "Your post method url") example: self.postLoginCall(url: "http://1.0.0.1/api/login.php")
func postLoginCall(url : String){
let request = NSMutableURLRequest(url: NSURL(string: url)! as URL)
request.httpMethod = "POST"
let postString = "emailaddress=\(userEmailID!)&password=\(Userpassword!)"
print(postString)
request.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
guard error == nil && data != nil else { // check for fundamental networking error
print("error=\(error)")
return
}
do {
if let responseJSON = try JSONSerialization.jsonObject(with: data!) as? [String:AnyObject]{
print(responseJSON)
print(responseJSON["status"]!)
self.response1 = responseJSON["status"]! as! Int
print(self.response1)
//Check response from the sever
if self.response1 == 200
{
OperationQueue.main.addOperation {
//API call Successful and can perform other operatios
print("Login Successful")
}
}
else
{
OperationQueue.main.addOperation {
//API call failed and perform other operations
print("Login Failed")
}
}
}
}
catch {
print("Error -> \(error)")
}
}
task.resume()
}
Hello everyone I share below an example of a function to make a request in POST with SWIFT 5+.
This function allows you to send a POST request with an API entry point and parameters in the form of [[String: String]] and an Int to determine the output action.
For the output actions we call a function with Switch Case.
The operation is extremely simple. You have to put the two functions in one of your classes.
func MGSetRequestApi(endpoint: String, parameters: [[String: String]], MGSetAction: Int) -> String {
var setReturn: String!
let semaphore = DispatchSemaphore (value: 0)
var MGGetParam: String! = ""
for gate in parameters {
for (key, value) in gate {
let myParam = key + "=" + value + "&"
MGGetParam.append(contentsOf: myParam)
}
}
let postData = MGGetParam.data(using: .utf8)
var request = URLRequest(url: URL(string: endpoint)!,timeoutInterval: 10000)
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = postData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
print(String(describing: error))
semaphore.signal()
return
}
print(String(data: data, encoding: .utf8)!)
setReturn = String(data: data, encoding: .utf8)!
DispatchQueue.main.async {
self.MGRequestAction(MGGetIdRq: MGSetAction, MGGetData: setReturn)
}
semaphore.signal()
}
task.resume()
semaphore.wait()
return setReturn
}
Then implement this function to manage the outputs
func MGRequestAction(MGGetIdRq: Int, MGGetData: String) {
switch MGGetIdRq {
case 1:
// Do something here
case 2:
// Do something else here
case 3:
// Do something else here again
default:
print("Set default action");
}
}
How to use this, you have two possibilities, the first one is to process what the function
MGSetRequestApi(endpoint: String, parameters: [[String: String]], MGSetAction: Int) -> String
returns (String) or to pass by the function
MGRequestAction(MGGetIdRq: Int, MGGetData: String)
which will call your Json parse function.
The MGRequestAction() function takes for parameter an Int for the choice of the action and the String of the return of the request
Now to use it do like this:
_ = MGSetRequestApi(endpoint: MY_END_POINT_API,
parameters: [["KEY_1": "VALUE 1"],
["KEY_2": "VALUE 2"],
["KEY_3": "VALUE 3"],
["KEY_4": "VALUE 4"]],
MGSetAction: 3)

How to change JSON POST request to handle HTTPS

Below is my login function. It's a JSON POST request and before, when the URL was http, it worked flawlessly. I attached a JSON filled with the username/password of the user. Today we added a SSL Certificate and after switching the URL to https, it produced this error:
NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9843)
I'm not really sure what's going on. I typed that error into google and didn't get any where. I appreciate any help, thank you!
func login(params : Dictionary<String, String>, url : String, postCompleted : (succeeded: Bool, msg: String) -> ()) {
var request = NSMutableURLRequest(URL: NSURL(string: url)!)
var session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(params, options: nil, error: &err)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
if response != nil {
if response.isKindOfClass(NSHTTPURLResponse) {
httpResponse = response as NSHTTPURLResponse
if let authorizationID = httpResponse.allHeaderFields["Authorization"] as String! {
Locksmith.saveData(["id":authorizationID], forUserAccount: currentUser, inService: "setUpAuthorizationId")
}
else {
println("Failed")
}
}
}
var err: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves, error: &err) as? NSDictionary
// Did the JSONObjectWithData constructor return an error? If so, log the error to the console
if(err != nil) {
println(err!.localizedDescription)
let jsonStr = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Error could not parse JSON: '\(jsonStr!)'")
postCompleted(succeeded: false, msg: "Error")
}
else {
// The JSONObjectWithData constructor didn't return an error. But, we should still
// check and make sure that json has a value using optional binding.
if let parseJSON = json {
// Okay, the parsedJSON is here, let's get the value for 'success' out of it
if let status = parseJSON["status"] as? String {
if let extractData = parseJSON["data"] as? NSDictionary {
let extractUserId:Int = extractData["id"] as Int
userId = extractUserId
}
if status == "success" {
postCompleted(succeeded: true, msg: "Logged in.")
} else {
let failMessage = parseJSON["message"] as? String
postCompleted(succeeded: false, msg: failMessage!)
}
}
return
}
else {
// Woa, okay the json object was nil, something went worng. Maybe the server isn't running?
let jsonStr = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Error could not parse JSON: \(jsonStr)")
postCompleted(succeeded: false, msg: "Error")
}
}
})
task.resume()
}
Using This awesome article I was able to fix my problem. All I needed to do was add:
NSObject, NSURLSessionDelegate, NSURLSessionTaskDelegate
after my class name, and then add these two delegates:
func URLSession(session: NSURLSession,
didReceiveChallenge challenge:
NSURLAuthenticationChallenge,
completionHandler:
(NSURLSessionAuthChallengeDisposition,
NSURLCredential!) -> Void) {
completionHandler(
NSURLSessionAuthChallengeDisposition.UseCredential,
NSURLCredential(forTrust:
challenge.protectionSpace.serverTrust))
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, willPerformHTTPRedirection response: NSHTTPURLResponse, newRequest request: NSURLRequest, completionHandler: (NSURLRequest!) -> Void) {
var newRequest : NSURLRequest? = request
println(newRequest?.description);
completionHandler(newRequest)
}
after that in my actual request I just needed to change:
var session = NSURLSession.sharedSession()
to:
var configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
var session = NSURLSession(configuration: configuration, delegate: self, delegateQueue:NSOperationQueue.mainQueue())
hope this helps someone!!