not able to parse the JSON using JSON decoder - ios

I am new to swift and i am trying to integrate the CCAvenue payment gateway. I am hitting the server to get the payment option list from the CCAvenue server which i an getting in the response but i am not able to parse the data into a JSON object, it is throwing some exception. Thanks in advance for the help
here is my code
override func viewDidLoad() {
super.viewDidLoad()
let urlAsString = "https://test.ccavenue.com/transaction/transaction.do?"
let myRequestString = "command=\(COMMAND)&currency=\(currency)&amount=\(amount)&access_code=\(accessCode)&customer_identifier=\(customerIdentifier)"
let myRequestData = NSData.init(bytes: myRequestString.cString(using: .utf8), length: myRequestString.count) as Data
let request = NSMutableURLRequest.init(url: URL(string: urlAsString)!)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "content-type")
request.setValue(urlAsString, forHTTPHeaderField: "Referer")
request.setValue("Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", forHTTPHeaderField: "User-Agent")
request.httpMethod = "POST"
request.httpBody = myRequestData
let requestData = URLSession.shared.dataTask(with: request as URLRequest) { (data, response, error) in
let responseData = NSString.init(data: data!, encoding: String.Encoding.ascii.rawValue)
if error == nil {
print("\(responseData)")
}
// if request is error free then decode the json using json decoder and assigning the values to the array
guard let data = data else {return}
do{
let a = try JSONDecoder().decode([CCPaymentOption].self, from: data)
print("\(String(describing: a))")
}catch {
print("Error")
}
}.resume()
print("\(requestData)")
}
this is what i am getting in my debugger
Optional({"payOptions":[{"payOpt":"OPTCRDC","payOptDesc":"Credit Card","cardsList":"[{\"cardName\":\"Diners Club\",\"cardType\":\"CRDC\",\"payOptType\":\"OPTCRDC\",\"payOptDesc\":\"Credit Card\",\"dataAcceptedAt\":\"CCAvenue\",\"status\":\"ACTI\"},{\"cardName\":\"MasterCard\",\"cardType\":\"CRDC\",\"payOptType\":\"OPTCRDC\",\"payOptDesc\":\"Credit Card\",\"dataAcceptedAt\":\"CCAvenue\",\"status\":\"ACTI\",\"statusMessage\":\"\"},{\"cardName\":\"Visa\",\"cardType\":\"CRDC\",\"payOptType\":\"OPTCRDC\",\"payOptDesc\":\"Credit Card\",\"dataAcceptedAt\":\"CCAvenue\",\"status\":\"ACTI\"}]"},{"payOpt":"OPTDBCRD","payOptDesc":"Debit Card","cardsList":"[{\"cardName\":\"MasterCard Debit Card\",\"cardType\":\"DBCRD\",\"payOptType\":\"OPTDBCRD\",\"payOptDesc\":\"Debit Card\",\"dataAcceptedAt\":\"CCAvenue\",\"status\":\"ACTI\"},{\"cardName\":\"Visa Debit Card\",\"cardType\":\"DBCRD\",\"payOptType\":\"OPTDBCRD\",\"payOptDesc\":\"Debit Card\",\"dataAcceptedAt\":\"CCAvenue\",\"status\":\"ACTI\"}]"},{"payOpt":"OPTNBK","payOptDesc":"Net Banking","cardsList":"[{\"cardName\":\"AvenuesTest\",\"cardType\":\"NBK\",\"payOptType\":\"OPTNBK\",\"payOptDesc\":\"Net Banking\",\"dataAcceptedAt\":\"CCAvenue\",\"status\":\"ACTI\",\"statusMessage\":\"\"}]"}]})
Error

There are many points needed to be checked while decoding the object.
Make sure your CCPaymentOption Model addopt the Protocol Codable.
While Decoding the Data , make sure you are aware of thing that your response is in form of Dictionary or Array
lets say you are getting the Array in response , in that case you can directly use [CCPaymentOption] in JSONDecoder().decode() method.
And if you are getting the Dictionary from the server response then you need to decode the object on that way.
Example of CCPaymentOption Model for point no 3.
struct CCPaymentOption : Codable {
var amount:String // Note down that , please use exact same
// key as you are getting from server side.
}
do {
let arrPaymentOptions = try JSONDecoder().decode([CCPaymentOption].self, from: responseData)
print(arrPaymentOptions)
///... Array of Your Model reference.
} catch {
print(error)
}
Example of CCPaymentOption Model for point no 4.
struct CCPaymentOption : Codable {
var amount:String
}
struct responseDictionary : Codable {
var paymentOption:[CCPaymentOption] // Note down that , please
// use exact same key as you
// are getting from server
// side.
}
do {
let responseDict = try JSONDecoder().decode(responseDictionary.self, from: responseData)
print(responseDict.paymentOption)
// responseDict.paymentOption is the Array of Your
// Model reference.
} catch {
print(error)
}
Please try out the below one if you don't want to use the JSON decoder. :-
URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
guard error == nil else { return }
guard let responseData = data else { return }
do {
if let jsonObject = try JSONSerialization.jsonObject(with: responseData, options: []) as? [String:Any] {
///... All you want is here jsonObject is the Dictionary (required , not an optional)
}
} catch {
print(error)
}
}).resume()
After getting the data from server side , you need to do JSONSerialization with the help of native method. This method will return the Dictionary or Array (Depending on your server response.)

Related

swift JSON login REST with post and get response example

It's my first experience with REST in iOS development with swift. I couldn't find any working or straight (simple) example for doing what i need here.
I have a login backend (https://myaddress.com/rest/login), where I need to pass 2 params: login and password. When I pass good values (user exists in database) I get 2 variables as a result: token (string) and firstLogin (bool). So when I get those values I know that login is successful and I can log in into my app.
So I am begging you for an example (just a simple function) of how to achieve that. If I get working code example I will know how to use it for other rest services in my app. I tried many solutions from tutorials I found, but any of them was working for me.. So to not waste my time searching I would like someone experienced to show me the way to achieve that.
I am not sure if Alamofire is so good to use, I know that swift 4 has it's own build neetwork services and to work with json. Any solution that works would be great.
Also, side question - if I would prefer to use Alamofire, do I need to use swiftyJSON also? Or it's just for parsing?
You can use URLSession if you don't like to import Alamofire in your Project to perform a simple task.
here are some method : GET, POST, DELETE METHODS and tutorial
GET METHOD
func makeGetCall() {
// Set up the URL request
let todoEndpoint: String = "https://jsonplaceholder.typicode.com/todos/1"
guard let url = URL(string: todoEndpoint) else {
print("Error: cannot create URL")
return
}
let urlRequest = URLRequest(url: url)
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// make the request
let task = session.dataTask(with: urlRequest) {
(data, response, error) in
// check for any errors
guard error == nil else {
print("error calling GET on /todos/1")
print(error!)
return
}
// make sure we got data
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let todo = try JSONSerialization.jsonObject(with: responseData, options: [])
as? [String: Any] else {
print("error trying to convert data to JSON")
return
}
// now we have the todo
// let's just print it to prove we can access it
print("The todo is: " + todo.description)
// the todo object is a dictionary
// so we just access the title using the "title" key
// so check for a title and print it if we have one
guard let todoTitle = todo["title"] as? String else {
print("Could not get todo title from JSON")
return
}
print("The title is: " + todoTitle)
} catch {
print("error trying to convert data to JSON")
return
}
}
task.resume()
}
POST METHOD
func makePostCall() {
let todosEndpoint: String = "https://jsonplaceholder.typicode.com/todos"
guard let todosURL = URL(string: todosEndpoint) else {
print("Error: cannot create URL")
return
}
var todosUrlRequest = URLRequest(url: todosURL)
todosUrlRequest.httpMethod = "POST"
let newTodo: [String: Any] = ["title": "My First todo", "completed": false, "userId": 1]
let jsonTodo: Data
do {
jsonTodo = try JSONSerialization.data(withJSONObject: newTodo, options: [])
todosUrlRequest.httpBody = jsonTodo
} catch {
print("Error: cannot create JSON from todo")
return
}
let session = URLSession.shared
let task = session.dataTask(with: todosUrlRequest) {
(data, response, error) in
guard error == nil else {
print("error calling POST on /todos/1")
print(error!)
return
}
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let receivedTodo = try JSONSerialization.jsonObject(with: responseData,
options: []) as? [String: Any] else {
print("Could not get JSON from responseData as dictionary")
return
}
print("The todo is: " + receivedTodo.description)
guard let todoID = receivedTodo["id"] as? Int else {
print("Could not get todoID as int from JSON")
return
}
print("The ID is: \(todoID)")
} catch {
print("error parsing response from POST on /todos")
return
}
}
task.resume()
}
DELETE METHOD
func makeDeleteCall() {
let firstTodoEndpoint: String = "https://jsonplaceholder.typicode.com/todos/1"
var firstTodoUrlRequest = URLRequest(url: URL(string: firstTodoEndpoint)!)
firstTodoUrlRequest.httpMethod = "DELETE"
let session = URLSession.shared
let task = session.dataTask(with: firstTodoUrlRequest) {
(data, response, error) in
guard let _ = data else {
print("error calling DELETE on /todos/1")
return
}
print("DELETE ok")
}
task.resume()
}
Thanks #MAhipal Singh for you answer. I'll post here example with Alamafire that I used so it's all in one stack question. It's easier than I though, solutions I tried to use before were not working cause I had problems with pinning certificate about I forgot..
func loginRest(login:String, password:String, deviceId:String){
let urlStr = restServices.REST_MAIN_URL + restServices.REST_LOGIN
let params = ["login":login, "password":password, "deviceId":deviceId]
let paramsJson = try! JSONSerialization.data(withJSONObject: params)
var headers: HTTPHeaders = ["Content-Type": "application/json"]
Alamofire.request(urlStr, method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers).responseJSON { (response) in
switch response.result {
case .success:
print("SUKCES with \(response)")
case .failure(let error):
print("ERROR with '\(error)")
}
}
If the post is proper the response is (console print):
SUKCES with SUCCESS: {
firstLogin = 1;
token = "dfkafjkfdsakfadsjfksjkfaadjfkjdfkjfskjfdkafjakfjakfjsafksjdafjy878328hjh";
}

iOS Swift POST param and body

I'm trying to POST a key parameter and a JSON dictionary body to an API but for some reason it won't work.
Here's my work so far.
#IBAction func POST(_ sender: Any) {
let url = URL(string: "http://apilink.com/updateProfile&=param")!
let jsonObject = ["FName":"Tarik",
"LName":"Salama"]
let jsonData = try! JSONSerialization.data(withJSONObject: jsonObject, options: [])
var request = URLRequest(url: url)
request.httpMethod = "post"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.httpBody = jsonData
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
print("error:", error)
return
}
do {
guard let data = data else { return }
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: AnyObject] else { return }
print("json:", json)
} catch {
print("error:", error)
}
}
task.resume()
}
I get this error when I click the POST button:
error: Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}
What am I doing wrong?
I tried changing some stuff around after I checked some other threads like putting the parameter and the data dictionary into a key:value dictionary but the API responded and said that the data is invalid which means the whole thing was sent in an incorrect format.
Note: I'm not allowed to use 3rd party libraries like Alamofire and the API link is working fine as shown in the screenshot.
you can check the problem by printing the response data
do {
guard let data = data else { return }
print(String(data: data, encoding: .utf8) ?? "No Conversion") //Print Here
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: AnyObject] else { return }
print("json:", json)
} catch {
print("error:", error)
}
look at the 'Print Here' line. I believe the response is not a valid json so when you try to parse the data to JSOn it always ends up in catch block.

JSON in NSData objects for common rest API

I'm trying to send data from my app to a rest API that is also being used by an Android app developed by another programmer. I have the JSON being converted into an NSData object using NSJSONSerialization.dataWithJSONObject and then attaching it to a NSURLRequest but the NSData object is a hexadecimal representation of the JSON String. According to the other developer his Android code is creating and transmitting the JSON object in UTF-8 encoding, so my question is how do I either send the JSON string as UTF-8 text or what is the best way to make the API able to handle both sources as seamlessly as possible?
EDIT: The code that I'm using now
func postToServer() {
let endPoint: String = "http://server.com"
guard let url = NSURL(string: endPoint) else {
print("ERROR: cannot create URL")
return
}
let urlRequest = NSMutableURLRequest(URL: url)
urlRequest.HTTPMethod = "POST"
urlRequest.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
let loc = self.getLocation()
var content:[String: AnyObject] = ["action": "put-point", "request": ["rangeKey": self.id, "lng": loc.coordinate.longitude, "lat": loc.coordinate.latitude, "count": self.count]]
var data: NSData! = NSData()
do {
data = try NSJSONSerialization.dataWithJSONObject(content, options: NSJSONWritingOptions())
print(data)
} catch {
print ("Error")
}
urlRequest.HTTPBody = data
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(urlRequest, completionHandler:{ data, response, error in
guard error == nil else {
print("ERROR: Cannot call Get on endpoint")
print(error)
return
}
guard let responseData = data else {
print("ERROR: Did not receive any data")
return
}
print("DATA: \(data)")
})
task.resume()
}
You could do something like
let jsonObj = [...]
var data = NSData()
do {
data = try NSJSONSerialization.dataWithJSONObject(jsonObj, options: .PrettyPrinted)
let dataString = NSString(data: data, encoding: NSUTF8StringEncoding)!
} catch {
print("error: \(error)")
}
*Tried on Swift 2 & Xcode 7.3.1

Parse JSON response with SwiftyJSON without crash

My iOS app is getting JSON response from server
let myURL = NSURL(string: SERVER_URL);
let request = NSMutableURLRequest(URL:myURL!);
request.HTTPMethod = "POST";
let postString = ""
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
{
data, response, error in
if error != nil {
print("error=\(error)")
return
}
dispatch_async(dispatch_get_main_queue(),{
var json = JSON(data: data!)
let someInt = json["someInt"].int
let message = json["message"].stringValue
Sometimes server is down or there may be errors in JSON so there will be no such values (message, someInt) and I want to handle it without app crash - what can I do?
With SwiftyJSON, non-optional getters end with Value, and optional getters don't.
So to test if the value is here you can use optional binding with if let:
if let someInt = json["someInt"].int,
message = json["message"].string {
// someInt and message are available here
} else {
// someInt and message are not available
}
Or with guard:
guard let someInt = json["someInt"].int,
message = json["message"].string else {
// error, someInt and message are not available
return
}
// someInt and message are available here
Very simple, probably you already know it, you could protect your code with:
if let someInt = json["someInt"].int {
// do whatever you want with someInt
}
if let message = json["message"].string {
// do whatever you want with message
}
Try this approach:
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if let data = data,
jsonString = NSString(data: data, encoding: NSUTF8StringEncoding)
where error == nil {
var json = JSON(data: data!)
// use some protection as explained before..
} else {
print("error=\(error!.localizedDescription)")
}
}
task.resume()
Let me post my answer too =)
first of all you can implement small extension for failure JSON initializer:
extension JSON {
init?(_ data: NSData?) {
if let data = data {
self.init(data: data)
} else {
return nil
}
}
}
You may put it in global scope with SwiftyJSON imported and forget about forcing unwrap your data before use it in JSON. Same fail initializers can be written for other data types if you use them. Its only for a bit shorter and readable code in future. With many routes or in some cases, for example when you wait from json some single fields, this extension can make your code looks extremely easy and readable:
guard let singleMessage = JSON(data: data)?["message"].string else {return}
Then you need to check for nil in way that you need (in fact explained in previous answers). Probably you searching for fully valid data, so use if-let chain:
let myURL = NSURL(string: SERVER_URL);
let request = NSMutableURLRequest(URL:myURL!);
request.HTTPMethod = "POST";
let postString = ""
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
{
data, response, error in
if error != nil {
print("error=\(error)")
return
}
dispatch_async(dispatch_get_main_queue()) {
if let json = JSON(data: data),
someInt = json["someInt"].int,
message = json["message"].string,
// ...
{
// all data here, do what you want
} else {
print("error=\(error)")
return
}
}
}
The best would be to handle using try catch
request.HTTPBody = postdata.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
{
(data, response, error) in
dispatch_async(dispatch_get_main_queue(), {
var jsondata: AnyObject?
do
{
let jsondata = try NSJSONSerialization.JSONObjectWithData(data!, options: [])
print(jsondata)
// your code here
}
catch
{
print("Some Error Found")
}
})
}
task.resume()
If you encounter any error, you will receive a message in the console, thus preventing the application from crashing

Create and send json data to server using swift language and iOS 9+

I really need a code for send and receive data from server with JSON, i find a really good code but it isn't compatible with iOS9.
#IBAction func submitAction(sender: AnyObject) {
//declare parameter as a dictionary which contains string as key and value combination.
var parameters = ["name": nametextField.text, "password": passwordTextField.text] as Dictionary<String, String>
//create the url with NSURL
let url = NSURL(string: "http://myServerName.com/api") //change the url
//create the session object
var session = NSURLSession.sharedSession()
//now create the NSMutableRequest object using the url object
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "POST" //set http method as POST
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(parameters, options: nil, error: &err) // pass dictionary to nsdata object and set it as request body
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
//create dataTask using the session object to send data to the server
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
println("Response: \(response)")
var strData = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Body: \(strData)")
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)'")
}
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
var success = parseJSON["success"] as? Int
println("Succes: \(success)")
}
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)")
}
}
})
task.resume() }
Really thanks for the help
Swift syntax changed a little bit, but not significantly to break the whole code.
You will need to adjust few things like
println(err!.localizedDescription)
to
print(err!.localizedDescription)
Then your code will compile
Maybe have a look into the Alamofire Framework.
It really is making your life easier when it comes to handling HTTP requests.
Otherwise, as vadian suggested, check out the Swift 2 (do-try-catch) Errorhandling.
I have found a great tutorial Project from deege.
https://github.com/deege/deegeu-swift-rest-example
Here a breakdown of a HTTP request.
// Setup the session to make REST GET call. Notice the URL is https NOT http!! (if you need further assistance on how and why, let me know)
let endpoint: String = "https://yourAPI-Endpoint"
let session = NSURLSession.sharedSession()
let url = NSURL(string: endpoint)!
// Make the call and handle it in a completion handler
session.dataTaskWithURL(url, completionHandler: { ( data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
// Make sure we get an OK response
guard let realResponse = response as? NSHTTPURLResponse where
realResponse.statusCode == 200 else {
print("Not a 200 response")
return
}
// Read the JSON
do {
if let jsonString = NSString(data:data!, encoding: NSUTF8StringEncoding) {
// Print what we got from the call
print(jsonString)
// Parse the JSON
let jsonDictionary = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
let value = jsonDictionary["key"] as! String
}
} catch {
print("bad things happened")
}
}).resume()

Resources