How to change JSON POST request to handle HTTPS - json

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!!

Related

Static method requires that 'NSDictionary' conform to 'Encodable'

I am trying to make a http request function for calling an API I want to use. I keep getting the error that NSDictionary needs to conform to Encodable but in the initializations before the call Ive already made it encodable.
What am I doing wrong?
Error message
Static method 'apiRequest(method:params:url:authToken:)' requires that 'NSDictionary' conform to 'Encodable'
Below is the initialization for the API request parameters.
private struct APIBody<CallParams: Encodable>: Encodable {
var method: String
var params: CallParams
var jsonrpc = "2.0"
var id = Int64(Date().timeIntervalSince1970)
}
private static let bodyEncoder: JSONEncoder = {
let e = JSONEncoder()
e.keyEncodingStrategy = .convertToSnakeCase
return e
}()
Below is the initialization for the API request to be sent.
private static func apiRequest<Params: Encodable>(
method: String,
params: Params,
url: URL,
authToken: String?
) throws -> URLRequest {
let body = APIBody(method: method, params: params)
var req = URLRequest(url: url)
req.httpMethod = "POST"
do {
req.httpBody = try bodyEncoder.encode(body)
} catch let e {
assertionFailure("API encoding error: \(e)")
throw e
}
req.addValue("application/json", forHTTPHeaderField: "Content-Type")
req.addValue("application/json", forHTTPHeaderField: "Accept")
if let authToken = authToken, !authToken.isBlank {
req.addValue(authToken, forHTTPHeaderField: "X-Lbry-Auth-Token")
}
return req
}
Below is the final call to the API where I get the error.
static func apiCall(
method: String,
params: [String: Any],
connectionString: String,
authToken: String? = nil,
completion: #escaping ([String: Any]?, Error?) -> Void
) {
let req: URLRequest
do {
req = try apiRequest( //<-- Error throws on this Line *********************
method: method,
params: params as NSDictionary,
url: URL(string: connectionString)!,
authToken: authToken
)
} catch let e {
completion(nil, e)
return
}
let task = URLSession.shared.dataTask(with: req, completionHandler: { data, response, error in
guard let data = data, error == nil else {
// handle error
completion(nil, error)
return
}
do {
Log.verboseJSON.logIfEnabled(.debug, "Response to `\(method)`: \(String(data: data, encoding: .utf8)!)")
let response = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
if response?["result"] != nil {
completion(response, nil)
} else {
if response?["error"] == nil, response?["result"] == nil {
completion(nil, nil)
} else if response?["error"] as? String != nil {
completion(nil, LbryApiResponseError(response?["error"] as! String))
} else if let errorJson = response?["error"] as? [String: Any] {
completion(nil, LbryApiResponseError(errorJson["message"] as! String))
} else {
completion(nil, LbryApiResponseError("unknown api error"))
}
}
} catch {
completion(nil, error)
}
})
task.resume()
}

JSON Parsing sometimes crashes Swift

I am parsing JSON in my iOS app and sometimes when the network connection is weak but isn’t gone, the app will crash while trying to parse the JSON, because it says it had an error while force unwrapping a nil.
The code I use for that is here.
//
// MessageModel.swift
// truthordare
//
// Created by Dustin Palmatier on 11/2/19.
// Copyright © 2019 Hexham Network. All rights reserved.
//
import UIKit
protocol MessageModelProtocol: class {
func itemsDownloaded(items: NSArray)
}
class MessageModel: NSObject, URLSessionDataDelegate {
//properties
weak var delegate: MessageModelProtocol!
let urlPath = "Redacted" //this will be changed to the path where service.php lives
let deleteUrl = "REDACTED"
func downloadItems(TYPE: String, IDENTIFIER: String) {
let url: URL = URL(string: urlPath)!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let postString = "type=\(TYPE)&identifier=\(IDENTIFIER)";
request.httpBody = postString.data(using: String.Encoding.utf8);
let defaultSession = Foundation.URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if error != nil {
print("Failed to download data")
}else {
print("Data downloaded")
self.parseJSON(data!)
}
}
task.resume()
}
func deleteItems(TYPE: String, SKU: String) {
let myUrl = URL(string: "https://truthordare.hexhamnetwork.com/api/92fFDd93D/erase.php");
var request = URLRequest(url:myUrl!)
request.httpMethod = "POST"// Compose a query string
let postString = "type=\(TYPE)&sku=\(SKU)";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if error != nil
{
print("error=\(error ?? "Empty" as! Error)")
return
}
}
task.resume()
}
func parseJSON(_ data:Data) {
var jsonResult = NSArray()
do{
jsonResult = try JSONSerialization.jsonObject(with: data, options: [.allowFragments, .mutableContainers]) as! NSArray
} catch let error as NSError {
print(error)
}
var jsonElement = NSDictionary()
let tables = NSMutableArray()
for i in 0 ..< jsonResult.count
{
jsonElement = jsonResult[i] as! NSDictionary
let table = TableModel()
//the following insures none of the JsonElement values are nil through optional binding
if let sku = jsonElement["SKU"] as? String,
let message = jsonElement["MESSAGE"] as? String
{
table.sku = sku
table.message = message
}
tables.add(table)
}
DispatchQueue.main.async(execute: { () -> Void in
self.delegate.itemsDownloaded(items: tables)
})
}
}
It crashes once it gets to
self.delegate.itemsDownloaded(items: tables)
It says that it received a nil while force unwrapping
To initialize the delegate I called this within the classes that were calling this.
messageModel.delegate = self
To avoid the crash reliably declare delegate as regular optional
weak var delegate: MessageModelProtocol?
and call it
self.delegate?.itemsDownloaded(items: tables)
In Swift 4+ it's highly recommended to use the Codable protocol and in any Swift version a completion handler rather than protocol / delegate.
And don't use NS... collection types in Swift at all. Use native types. And .mutableContainers / .allowFragments is pointless in Swift if the expected type is a collection type. Omit the parameter.

how to make HTTPRequest with json in swift

I am making an ios application. I am new to swift and not able to understand my code. can anyone please help me to understand what is going on with my code.
This is login application on adding email id if the email exist it should go to next view controller and if not then it should give error. I am getting difficulty in understanding my code .
Here is my code:
class checkLoginViewController: UIViewController {
#IBOutlet weak var checkUsernametextfield: UITextField!
#IBAction func checkUsernameButton(_ sender: UIButton) {
print("Clicked On SUbmit !!!!")
//Read Value from Text
let email = checkUsernametextfield.text
let myUrl = URL(string: "http://192.168.0.117/rest/signup.php");
var request = URLRequest(url:myUrl!)
request.httpMethod = "POST"// Compose a query string
let postString = "email=\(String(describing: email))";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if error != nil
{
print("error=\(String(describing: error))")
return
}
// You can print out response object
print("response = \(String(describing: response))")
//Let's convert response sent from a server side script to a NSDictionary object:
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
// Now we can access value of First Name by its key
let emailValue = parseJSON["email"] as? String
print("email: \(String(describing: emailValue))")
}
} catch {
print(error)
}
}
task.resume()
Output:
Clicked On SUbmit !!!! response = Optional( { URL: http://192.168.0.117/rest/signup.php } { Status
Code: 200, Headers {
Connection = (
"Keep-Alive"
);
"Content-Length" = (
61
);
"Content-Type" = (
"application/json"
);
Date = (
"Mon, 12 Mar 2018 06:35:58 GMT"
);
"Keep-Alive" = (
"timeout=5, max=100"
);
Server = (
"Apache/2.4.27 (Ubuntu)"
); } }) email: nil
Maybe try this. Hope it works.
let url = URL(string:"http://192.168.0.117/rest/signup.php")
let parameters = ["email": checkUsernametextfield.text]
var request = URLRequest(url : url!)
request.httpMethod = "POST"
request.httpBody = try? JSONSerialization.data(withJSONObject:parameters, options: [])
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let session = URLSession.shared
session.dataTask(with: request, completionHandler: { (data, response, error) in
if let data = data {
do {
let json = try? JSONSerialization.jsonObject(with: data, options: []) as! Dictionary<String, Any>
if let json = json {
print("HERE SHOULD BE YOUR JSON \(json)")
}
}
} else {
print("Error \(String(describing: error?.localizedDescription))")
}
}).resume()
Here is way to send request.
enter code here
static func downloadConfig(url:URL, completion:#escaping (_ sucess:Bool , _ jsonObject: [String: String]?)->() ) {
var request = URLRequest(url: url)
request.setValue("application/x-www-form-urlencoded",
forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
let postString = "id=13&name=Jack"
request.httpBody = postString.data(using: .utf8)
URLSession.shared.dataTask(with: request) { (data,response,error) in
if let data = data ,let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200 {
do {
if let todoJSON = try JSONSerialization.jsonObject(with: data, options: []) as? [String: String]{
completion(true,todoJSON)
}
else
{
completion(false,nil)
}
}
catch {
//erro parsing
completion(false,nil)
}
}
else
{
completion(false,nil)
}
}.resume()
}
use this download json function in this way.
//Download Json File
let base_url = "base_url"
let urlstr = String.init(format: "%#", base_url)
let url = URL(string: urlstr)
GameUtil.downloadConfig(url: url!) {
(sucess: Bool , jsonObject: [String:String]?) in
if sucess , jsonObject != nil
{
self.configJson = jsonObject!
}
}

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)

JSON parse error: The data couldn’t be read because it isn’t in the correct format

I am new to Xcode and Swift and have the below code that shows error on print(parseJSON).
Error: The data couldn’t be read because it isn’t in the correct
format.
The code is posting data correctly to the server, but the response cannot be parsed.
JSON response from my ASMX page:
{"status":"Success","message":"User is registered"}
Any help will be appreciated.
// Send data to server side
let myURL = NSURL(string: "http://www.examle.com/Info.asmx/Testing")
let request = NSMutableURLRequest(URL: myURL!)
request.HTTPMethod = "POST"
let postString = "Email=\(userEmail!)&Password=\(userPassword!)"
print(postString)
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){
data, response, error in
if error != nil {
print("error=\(error)")
return
}
//var err: NSError?
do{
if let parseJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary{
print(parseJSON) // ERROR HERE
let resultValue = parseJSON["status"] as? String
print("result: \(resultValue)")
var isUserRegistered:Bool = false
if (resultValue == "Success") {
isUserRegistered = true
}
var messageToDisplay:String = parseJSON["message"] as! String!
if (!isUserRegistered){
messageToDisplay = parseJSON["message"] as! String!
}
dispatch_async(dispatch_get_main_queue(), {
let myAlert = UIAlertController(title: "Alert", message: messageToDisplay, preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default){ action in
self.dismissViewControllerAnimated(true, completion: nil)
}
myAlert.addAction(okAction)
self.presentViewController(myAlert, animated: true, completion: nil)
})
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
task.resume()