Swift 3 HTTP Request post with parameter error JSON format - json

I'm trying to send a http request using swift 3
it started like this with 2.3 and it worked well
func addBill(){
let parameter:[String:AnyObject] =
[
"app":"mpbid",
"token":userDefault.getToken()!,
"ver":1,
"data":
[
[
"productId":String(self.product!.productId!),
"customerId":self.nomorTeleponField.text!
]
]
]
HTTPRequest.addBill(parameter, startRequest: {
}) { (responseType, message, data) in
if responseType == HTTPRequest.responseType.Success{
if let status = data!["status"] as? String where status == "OK"{
if let datas = data!["data"] as? NSArray where datas.count > 0{
let errorResponse = datas[0]["errorStatus"] as! String
(errorResponse.characters.count) ")
if errorResponse.characters.count == 0 {
self.inquiryData = datas[0] as? [String:AnyObject]
self.performSegueWithIdentifier("showConfirmation", sender: self)
it succeed with a status 200 OK
but after swift 3 migration, i've turned the source code to this
func addBill(){
let parameter:[String:AnyObject] =
[
"app":"mpbid" as AnyObject,
"token":userDefault.getToken()! as AnyObject,
"ver":1 as AnyObject,
"data":
[
[
"productId":String(describing: self.product!.productId!),
"customerId":self.nomorTeleponField.text!,
"productCode":String(describing: self.product!.productCode!)
],
] as AnyObject,
]
HTTPRequest.addBill(parameter, startRequest: {
}) { (responseType, message, data) in
if responseType == HTTPRequest.responseType.success{
if let status = data!["status"] as? String, status == "OK"{
if let datas = data!["data"] as? NSArray, datas.count > 0{
let errorResponse = (datas[0] as! [String:AnyObject])["errorStatus"] as! String(errorResponse.characters.count) ")
if errorResponse.characters.count == 0 {
self.inquiryData = datas[0] as? [String:AnyObject]
self.performSegue(withIdentifier: "showConfirmation", sender: self)
It gave me status 500 internal server error
I guess I got wrong JSON format on sending the parameter using swift 3, any help would be really appreciated... thanks before

Related

Swift read from JSON dictionary

I am sending an Alamofire request and inside of my completion handler I have:
if let jsonData = response.result.value {
result = jsonData
guard let data = result.data(using: .utf8) else { return}
guard let dictionary = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
print("Could not cast JSON content as a Dictionary<String, Any>")
return
}
print("dictionary: \(dictionary)")
if dictionary["status"] as! String == "false"{
//Do something
}
}
else{
result = "\(response.error)"
}
The result of printing dictionary is ["status":false, "value":A1]. Ultimately I want to use status for my if statement. However I get a crash on the if statement line: if dictionary["status"] as! String == "false" of Fatal error: Unexpectedly found nil while unwrapping an Optional value. I also tried changing the line to if dictionary["status"] as! Bool == false and I get the exact same error.
The json as returned from the request is:
{
"value": "A1",
"status": "false"
}
So my question is, what is the correct way to get the value for status out of dictionary?
Would something like this work?
struct jsonOut: Codable {
let value: String
let status: String
}
if let jsonData = response.result.value {
result = jsonData
guard let data = result.data(using: .utf8)
let status = try JSONDecoder().decode(jsonOut.self, from: data)
}
Since the JSON has the format:
{
"value": "A1",
"status": "false"
}
The correct way is using Codable with the same format as the JSON:
struct jsonOut: Codable {
let value: String
let status: String
}
if let jsonData = response.result.value {
result = jsonData
guard let data = result.data(using: .utf8)
let statusData = try JSONDecoder().decode(jsonOut.self, from: data)
print("status: \(statusData.status)"
}

Swift Parsing decode 2 different json with 1 url api

Hi im new in swift and im kinda still learning, so i try to make login controller and parse a json data if it corrects it parse a json data with id and stuff and if login is failed than the json will show a kinda message. i already make a struct for all the value data that required but i got this error that said its nil.
so, this is the json if the login is success :
[
{
"id": 891,
"name": "User",
"email": "qdpim#immobisp.com",
"status": "1"
} ]
and this is the json if login is failed :
[
{
"message": "Login Failed..",
"status": "0"
} ]
so basicly it has a same url i guess? but i dont know im kinda stuck in here and i need help
struct login : Codable {
let id : Int
let name : String
let email : String
let status : String
let message : String
init(dictionary : [String : Any]) {
id = (dictionary ["id"] as? Int)!
name = (dictionary ["name"] as? String)!
email = (dictionary ["email"] as? String)!
status = (dictionary ["status"] as? String)!
message = (dictionary ["message"] as? String)!
}
enum CodingKeys : String, CodingKey {
case id = "id"
case name = "name"
case email = "email"
case status = "status"
case message = "message"
}
}
func Login() {
let Email = EmailField.text!
let Pass = PasswordField.text!
print(api)
guard let JsonUrl = URL(string: api) else {return}
URLSession.shared.dataTask(with: JsonUrl) { (data, response, error) in
guard let data = data else {return}
do{
let parsing = try JSONDecoder().decode([login].self, from: data)
print(parsing)
self.Loginnn = parsing
let stats = self.Loginnn.map { $0.status}
if stats.contains("1"){
print("Login Success")
DispatchQueue.main.async {
self.appDelegate.loginSeque()
}
}else if stats.contains("0") {
let action = UIAlertAction(title: "Got It", style: .default, handler: nil)
let alert = UIAlertController(title: "Wrong Email / Password", message: "Please Try Again ", preferredStyle: .alert)
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
// so basicly i wanna run this alert action by search status if its contains "0"
}
}
}catch{
print(error)
}
}.resume()
}
so when i try to test to failed my login, i doesnt show the message in my json in my log, instead it show this error
"keyNotFound(CodingKeys(stringValue: "id", intValue: nil),
Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index
0", intValue: 0)], debugDescription: "No value associated with key
CodingKeys(stringValue: \"id\", intValue: nil) (\"id\").",
underlyingError: nil))"
i just wanna pop some message or alert if the login is failed because or wrong password or email.....so maybe can someone help me how to do it the best way?
You can declare Success and Failure response types as below,
struct LoginSuccess: Decodable {
var id: Int
var name: String
var email: String
var status: String
}
struct LoginFailure: Decodable {
var status: String
var message: String
}
and then use as,
guard let JsonUrl = URL(string: api) else { return }
URLSession.shared.dataTask(with: JsonUrl) { (data, response, error) in
guard let data = data else { return }
if let success = try? JSONDecoder().decode([LoginSuccess].self, from: data).first {
GlobalVariable.UserId = String(success.id)
DispatchQueue.main.async {
self.appDelegate.loginSeque()
}
} else if let failure = try? JSONDecoder().decode([LoginFailure].self, from: data).first {
let action = UIAlertAction(title: "Got It", style: .default, handler: nil)
let alert = UIAlertController(title: "Wrong Email / Password", message: failure.message, preferredStyle: .alert)
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
}
}.resume()
In this situation I would use JSONSerialization to decode the data to a [[String: Any]] and look at the content to determine what kind of message it is.
In my code I have assumed the "status" item tells us if it was a successful login or not but one could for instance look for the presence of "id" or the count of elements in the dictionary as well to determine the type of response
do {
let result = try JSONSerialization.jsonObject(with: data) as! [[String: Any]]
if let response = result.first, let status = response["status"] as? String {
if status == "1" {
if let id = response["id"] as? Int {
let ids = String(id)
//...
}
} else {
if let message = response["message"] as? String {
print(message)
}
}
}
} catch {
print(error)
}
Below is my solution used in the code from your question. Note that I have simplified the Login struct since it is only used when login was successful
struct Login {
let id : Int
let name : String
let email : String
}
do {
let result = try JSONSerialization.jsonObject(with: data) as! [[String: Any]]
if let response = result.first, let status = response["status"] as? String {
if status == "1" {
//handle success
let login = Login(id: response["id"] as? Int ?? 0,
name: response["name"] as? String ?? "",
email: response["email"] as? String ?? "")
self.Loginnn = login
DispatchQueue.main.async {
self.appDelegate.loginSeque()
}
} else {
let action = UIAlertAction(title: "Got It", style: .default, handler: nil)
let alert = UIAlertController(title: "Wrong Email / Password", message: "Please Try Again ", preferredStyle: .alert)
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
}
}
} catch {
print(error)
}
The success response only contains the keys ("id", "name", "email", "status")
[ { "id": 891, "name": "User", "email": "qdpim#immobisp.com", "status": "1" } ]
and the failure response only contains the keys ("message", "status")
[ { "message": "Login Failed..", "status": "0" } ]
If you want to use the same struct for both JSON responses, you should make the properties optional
struct login : Codable {
var id: Int?
var name: String?
var email: String?
var status: String?
var message: String?
}
Also, since your keys are the same as your properties, you don't need enum CodingKeys or init for that matter if you use JSONDecoder().decode
You've already got an answer (or three) for this, but I want to show you how to do it without using JSONSerialization or speculative decoding.
So we have some LoginSuccess and LoginFailure types that you want to decode:
struct LoginSuccess: Decodable {
var id: Int
var name: String
var email: String
}
struct LoginFailure: Decodable {
var message: String
}
And we want to discriminate between them based on a status that is in the same container as the fields of those types. So we create an enum:
enum LoginResult: Decodable {
case success(LoginSuccess)
case failure(LoginFailure)
enum Keys: CodingKey {
case status
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Keys.self)
if try container.decode(String.self, forKey: .status) == "1" {
self = .success(try LoginSuccess(from: decoder))
} else {
self = .failure(try LoginFailure(from: decoder))
}
}
}
Note that the enum's init does not call decoder.decode(LoginSuccess.self). It passes the decoder it was given to the LoginSuccess initializer. Same with LoginFailure. This means those initializers will extract values from the same container as the status field.
Test:
let successData = #"[ { "id": 891, "name": "User", "email": "qdpim#immobisp.com", "status": "1" } ]"#.data(using: .utf8)!
print(try JSONDecoder().decode([LoginResult].self, from: successData))
// Output:
[__lldb_expr_1.LoginResult.success(__lldb_expr_1.LoginSuccess(id: 891, name: "User", email: "qdpim#immobisp.com"))]
let failureData = #"[ { "message": "Login Failed..", "status": "0" } ]"#.data(using: .utf8)!
print(try JSONDecoder().decode([LoginResult].self, from: failureData))
// Output:
[__lldb_expr_1.LoginResult.failure(__lldb_expr_1.LoginFailure(message: "Login Failed.."))]
Note that because your example data is wrapped in [...], I decoded arrays of LoginResult.

How to get value of jsonObject inside a jsonObject swift 4?

I use FCM to send push notification to my iOS app.When user click on the notification tray,the data handle by the function below:
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
print(userInfo['data'])
}
The userInfo is a [AnyHashable:Any] type.I successfully get the data from the userInfo['data'].So here is the data structure for userInfo['data'] :
'{"data":
{
"title":"My app",
"message":"The message here",
"payload":{
"post_id":"602"
},
"timestamp":"2018-03-10 14:12:08"
}
}'
Here is how I tried :
if let dataString = userInfo["data"] as? String {
let data = dataString.data(using: .utf8)!
do {
if let json = try JSONSerialization.jsonObject(with: data, options : .allowFragments) as? [String : Any]
{
let message = json["message"] as? String ?? "No message here"
let title = json["title"] as String ?? ""
//here is the problem..I have no idea to do it here
let payload = json["payload"] as? [String : Int] ?? [:]
for element in payload {
if let postId = element["post_id"] {
//print("postId = \(postId)")
}
}
} else {
print("bad json")
}
} catch let error as NSError {
print(error)
}
So as shown in above,I have no problem to get value of title,message and timestamp inside the data json.
But I have to idea how to get the value of post_id which is inside payload array.
So in this case,how to get the value of post_id from the data json above? Thanks.
Access post id like this
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
if let data = userInfo["data"] as? [String: Any],
let payload = data["payload"] as? [String: Any],
let postId = payload["post_id"] as? String{
print("post id \(postId)")
}else{
print("there is no post id inside the payload")
}
}

Error when parse json in swift 4

I want to get data from website by using JSON in my app. But the problem is that when I try to get the value of key ["posts"] in the JSON data, the error appears.
let url = NSURL(string: "http://bongdavn.com/category/du-lieu/?json=1")!
let request = NSMutableURLRequest(url: url as URL)
URLSession.shared.dataTask(with: request as URLRequest) { (data : Data?, urlResponse : URLResponse?, error : Error?) in
if error != nil {
}
do {
let json = try JSONSerialization.jsonObject(with: data!) as? [String : Any]
let post = json!["post"] as! [String : Any]
print(post)
let content = post["content"] // error show from here.
print(content)
// the compiler show nil instead of value of "content"
DispatchQueue.main.async {
self.TableView.reloadData()
}
}
catch {
print("Catch the error : \(error)")
}
}
.resume()
Here is my json data:
{
"status":"ok",
"post":{
"id":121,
"type":"post",
"slug":"epl-england-premier-league-anh",
"url":"http:\/\/bongdavn.com\/epl-england-premier-league-anh\/",
"status":"publish",
"title":"[EPL] England Premier League – Anh",
"title_plain":"[EPL] England Premier League – Anh",
"content":"<p>West Ham <strong><span class=\"hom\">1<\/span>\u00a0–\u00a0<\/strong><span class=\"awy\"><strong>1<\/strong>\u00a0<\/span>Leicester|||Crystal Palace\u00a0<strong><span class=\"hom\">2<\/span>\u00a0–\u00a0<\/strong><span class=\"awy\"><strong>1<\/strong>\u00a0<\/span>Stoke|||,
"date":"2017-11-29 09:44:13",
"modified":"2017-11-29 09:44:16",
"categories":[ ],
"tags":[ ],
"author":{ },
"comments":[ ],
"attachments":[ ],
"comment_count":0,
"comment_status":"open",
"custom_fields":{ }
},
"previous_url":"http:\/\/bongdavn.com\/bundesliga-dortmund-schalke\/"
}
Here is the error:
Could not cast value of type '__NSSingleObjectArrayI' (0x109b9c328) to 'NSDictionary' (0x109b9cf58).
2017-11-29 22:45:19.764169+0700 BongDa[713:17684] Could not cast value of type '__NSSingleObjectArrayI' (0x109b9c328) to 'NSDictionary' (0x109b9cf58).
(lldb)
Your json posted is wrong, according to what I get from the url, there is an array posts, so you can do something like:
do {
let json = try JSONSerialization.jsonObject(with: data!) as? [String : Any]
let posts = json!["posts"] as! [[String: Any]] // posts is an array
let post = posts.first! // get the first here, or according to your need
print(post)
let content = post["content"]
print(content)
DispatchQueue.main.async {
self.TableView.reloadData()
}
}
catch {
print("Catch the error : \(error)")
}
}
.resume()

Remote json parsing in Swift

[
-{
valid:"2",
invalid: "1",
pending: "2"
},
-{
valid:"0",
invalid: "1",
pending: "0"
},
-{
valid:"2",
invalid: "1",
pending: "2"
}
]
I am trying to parse this remote json and populate the data into an array.
I am struggling for hours trying to find out why my code isn't working,the array always ends up being empty. can somebody please tell me what am i doing wrong ?
var arrayreports : [Report] = []
var report = Report()
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let host = appDelegate.host
if(Reachability.isConnectedToNetwork()){
let postEndpoint: String = host+"/api/reportbyworkflow/7"
let session = NSURLSession.sharedSession()
let url = NSURL(string: postEndpoint)!
session.dataTaskWithURL(url, completionHandler: { ( data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
guard let realResponse = response as? NSHTTPURLResponse where
realResponse.statusCode == 201 else {
print("Bad thing happened")
return
}
do {
if let ipString = NSString(data:data!, encoding: NSUTF8StringEncoding) {
let jsonDictionary:AnyObject! = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
let json = jsonDictionary as? Array<AnyObject>
for index in 0...json!.count-1 {
let contact : AnyObject? = json![index]
print(contact)
let collection = contact! as! Dictionary<String, AnyObject>
let valid = collection["valid"] as! String
let invalid = collection["invalid"] as! String
let pending = collection["pending"] as! String
report!.valid = Double(Int(valid)!)
report!.invalid = Double(Int(invalid)!)
report!.pending = Double(Int(pending)!)
arrayreports.append(report!)
}
}}
catch {
print("bad things happened")
}
}).resume()
}
If your json is really the one you copied here, it is not valid ( check on jsonvalidator.com ).
So it is normal than your serialization returns an empty array