I am having trouble parsing some JSON in Swift. I am having trouble getting the errors variable it returns nil. I think it should be a dictionary?
Below is the JSON that is returned from my API as printed in the console.
{
error = "{\"name\":[\"The name has already been taken.\"],\"email\":[\"The email has already been taken.\"]}";
success = 0;
}
And here is the Swift code.
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
print(parseJSON)
let success = parseJSON["success"] as? Int
if(success == 1) {
let myAlert = UIAlertController(title: "Alert", message: "Registration successful", preferredStyle: UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default){
(action) in
self.dismiss(animated: true, completion: nil)
}
myAlert.addAction(okAction);
self.present(myAlert, animated: true, completion: nil)
} else {
let errors = parseJSON["error"] as? NSDictionary
if(errors != nil){
print("NOT NIL")
// self.displayAlertMessage()
}
}
}
} catch{
print(error)
}
EDIT
Here is the JSON thats is printed using David's code below.
This is the parseJSON printed to the console.
["error": {"name":["The name has already been taken."],"email":["The email has already been taken."]}, "success": 0]
Here is my full method with Davids updated code.
let task = URLSession.shared.dataTask(with: request) { (theData: Data?, response: URLResponse?, theError: Error?) in
DispatchQueue.main.async
{
//spinningActivity!.hide(true)
if theError != nil {
self.displayAlertMessage(theError!.localizedDescription)
return
}
do {
guard let parseJSON = try JSONSerialization.jsonObject(with: theData!) as? [String:Any] else {return}
//print(parseJSON)
let success = parseJSON["success"] as? Int
if(success == 1) {
let myAlert = UIAlertController(title: "Alert", message: "Registration successful", preferredStyle: UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default){
(action) in
self.dismiss(animated: true, completion: nil)
}
myAlert.addAction(okAction);
self.present(myAlert, animated: true, completion: nil)
} else {
guard let errors = parseJSON["success"] as? Int else {return}
print(errors)
}
} catch{
print(error)
}
}
}
task.resume()
There are several issues with your code that might be not causing the issue directly, but are bad practices. Don't use NSDictionary in Swift, use [String:Any] when decoding JSON responses and don't use .mutableContainers as it has no effect in Swift, the mutability is determined by the let or var keyword when declaring the variable.
Moreover, don't include console print as the JSON response, include the actual JSON response in your question, as Swift's print statement doesn't produce a valid JSON.
let apiErrorResponse = """
{
"error": {
"name": "The name has already been taken.",
"email": ["The email has already been taken."]
},
"success": 0
}
"""
func handleApiErrorResponse(){
do {
guard let parseJSON = try JSONSerialization.jsonObject(with: apiErrorResponse.data(using: .utf8)!) as? [String:Any] else {return}
let success = parseJSON["success"] as? Int
if(success == 1) {
let myAlert = UIAlertController(title: "Alert", message: "Registration successful", preferredStyle: UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default){
(action) in
self.dismiss(animated: true, completion: nil)
}
myAlert.addAction(okAction);
self.present(myAlert, animated: true, completion: nil)
} else {
guard let errors = parseJSON["error"] as? [String:Any] else {return}
print(errors)
}
} catch{
print(error)
}
}
handleApiErrorResponse()
Output:
"["name": The name has already been taken., "email": <__NSSingleObjectArrayI 0x608000019f80>(\nThe email has already been taken.\n)\n]\n"
Related
I use json api in my application. It can check a company does use electronic Invoice. I have a json data like that:
{
"ErrorStatus": null,
"Result": {
"CustomerList": [
{
"RegisterNumber": "6320036072",
"Title": "VATAN BİLGİSAYAR SANAYİ VE TİCARET ANONİM ŞİRKETİ",
"Alias": "urn:mail:defaultpk#vatanbilgisayar.com",
"Type": "Özel",
"FirstCreationTime": "2014-01-01T05:35:20",
"AliasCreationTime": "2014-01-01T05:35:20"
}
],
"ISEInvoiceCustomer": true
} }
and i use that fucntion for get json data:
func getClientQuery(authorization:String) {
let url = NSURL(string: URLCustomerCheck+strRegisterNumber)
let request = NSMutableURLRequest(url: url! as URL)
request.httpMethod = "GET"
request.addValue(authorization, forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request as URLRequest) { data,response,error in
if error != nil {
let alert = UIAlertController(title: "Error", message: error?.localizedDescription, preferredStyle: UIAlertControllerStyle.alert)
let okButton = UIAlertAction(title: "OK", style: UIAlertActionStyle.cancel, handler: nil)
alert.addAction(okButton)
self.present(alert, animated: true, completion: nil)
} else {
if data != nil {
do {
let jSONResult = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as! Dictionary<String,AnyObject>
DispatchQueue.main.async {
print(jSONResult)
let result = jSONResult["Result"] as! [String:AnyObject]
//let customerList = result["CustomerList"] as! [[String:AnyObject]]
let ISEInvoiceCustomer = String(describing: result["ISEInvoiceCustomer"])
self._lblISEinvoiceCustomer.text = " \(ISEInvoiceCustomer) "
}
} catch {
}
}
}
}
task.resume()
}
My question is how can i parse "RegisterNumber", "Title".. in "CustomerList"? It's a array that have a member. However i can not parse it in my function.
The customerList line you commented out is needed. Then iterate that array and pull out whatever values you want from each dictionary.
And you really should avoid us as! or any other forced unwrapping when working with JSON. You don't want your app to crash when you obtain unexpected data.
And never use String(describing:) to create a value you will display to a user. The result is inappropriate for display. It's only to be used for debugging purposes.
if let jSONResult = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String:Any]
DispatchQueue.main.async {
print(jSONResult)
if let result = jSONResult["Result"] as? [String:AnyObject],
let customerList = result["CustomerList"] as? [[String:Any]] {
for customer in customList {
let registrationNumber = customer["RegisterNumber"]
// and any others you need
}
let ISEInvoiceCustomer = result["ISEInvoiceCustomer"] as? Bool ?? false
self._lblISEinvoiceCustomer.text = ISEInvoiceCustomer) ? "Yes" : "No"
}
}
}
Better to Map json to Model , this become easy using Codable
import Foundation
struct Client: Codable {
let errorStatus: ErrorStatus?
let result: Result
enum CodingKeys: String, CodingKey {
case errorStatus = "ErrorStatus"
case result = "Result"
}
}
struct ErrorStatus: Codable {
}
struct Result: Codable {
let customerList: [CustomerList]
let iseInvoiceCustomer: Bool
enum CodingKeys: String, CodingKey {
case customerList = "CustomerList"
case iseInvoiceCustomer = "ISEInvoiceCustomer"
}
}
struct CustomerList: Codable {
let registerNumber, title, alias, type: String
let firstCreationTime, aliasCreationTime: String
enum CodingKeys: String, CodingKey {
case registerNumber = "RegisterNumber"
case title = "Title"
case alias = "Alias"
case type = "Type"
case firstCreationTime = "FirstCreationTime"
case aliasCreationTime = "AliasCreationTime"
}
}
// MARK: Convenience initializers
extension Client {
init(data: Data) throws {
self = try JSONDecoder().decode(Client.self, from: data)
}
init(_ json: String, using encoding: String.Encoding = .utf8) throws {
guard let data = json.data(using: encoding) else {
throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
}
try self.init(data: data)
}
}
Get customerList :
func getClientQuery(authorization:String) {
let url = NSURL(string: URLCustomerCheck+strRegisterNumber)
let request = NSMutableURLRequest(url: url! as URL)
request.httpMethod = "GET"
request.addValue(authorization, forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request as URLRequest) { data,response,error in
if error != nil {
let alert = UIAlertController(title: "Error", message: error?.localizedDescription, preferredStyle: UIAlertControllerStyle.alert)
let okButton = UIAlertAction(title: "OK", style: UIAlertActionStyle.cancel, handler: nil)
alert.addAction(okButton)
self.present(alert, animated: true, completion: nil)
} else {
if data != nil {
if let client = try? Client.init(data: data){
client.result.customerList.forEach { (customer) in
print(customer.registerNumber)
}
}
}
}
}
task.resume()
}
let data = resultData
do {
guard let JSONResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String : AnyObject],
let resultObject = JSONResult["Result"] as? [String : AnyObject],
let customerList = resultObject["CustomerList"] as? [Anyobject]
else { return }
// Loop the array of objects
for object in customerList {
let registerNumber = object["RegisterNumber"] as? String
let title = object["Title"] as? String
let alias = object["Alias"] as? String
let type = object["Type"] as? String
let firstCreationTime = object["FirstCreationTime"] as? String // Or as a DateObject
let aliasCreationTime = object["AliasCreationTime"] as? String // Or as a DateObject
}
let isEInvoiceCustomer = resultObject["ISEInvoiceCustomer"] as? Bool
} catch {
print(error)
}
I am using following code to login users first user enters password and id then using post request I am sending this information to the server.
let alert = UIAlertController(title: nil, message: "Please wait...", preferredStyle: .alert)
let message2 = "Please wait..."
var messageMutableString = NSMutableAttributedString()
messageMutableString = NSMutableAttributedString(string: message2 as String, attributes: [NSFontAttributeName:UIFont(name: "HelveticaNeue-Bold",size: 15.0)!])
alert.setValue(messageMutableString, forKey: "attributedMessage")
let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50))
loadingIndicator.hidesWhenStopped = true
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
loadingIndicator.startAnimating();
alert.view.addSubview(loadingIndicator)
present(alert, animated: true, completion: nil)
if Reachability.isConnectedToNetwork() == true {
var postString = GlobalVariable.globalIpAdress
postString.append("&userid=")
postString.append(userId.text!)
postString.append("&password=")
postString.append(password.text!)
postString.append("&parola=")
let urlString = postString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlFragmentAllowed)
var request = URLRequest(url: URL(string:urlString!)!)
request.httpMethod = "POST"
request.timeoutInterval=10
message="Request timed out"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
DispatchQueue.main.async {
self.dismiss(animated: false, completion: nil)
}
guard let data = data, error == nil else {
print("error=\(String(describing: error))")
DispatchQueue.main.async {
let alert = UIAlertView(title: "Uyarı!", message: self.message, delegate: nil, cancelButtonTitle: "OK")
alert.show()
}
return;
}
if let json = (try? JSONSerialization.jsonObject(with: data, options: [])) as? NSDictionary
{
print(json)
if (json["error"] as? String) != nil {}
if let messages = json["messages"] as? NSArray{
for item in messages {
if let description = item as? AnyObject {
if let text = description["text"] as? String {
self.message = text
}
}
}
}
if let tokenTemp = json["token"] as? String {
self.success=true
GlobalVariable.globalToken = tokenTemp
}
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(String(describing: response))")
}
let responseString = String(data: data, encoding: .utf8)
print("responseString = \(String(describing: responseString))")
DispatchQueue.main.async {
if(self.success == true){
self.performSegue(withIdentifier: "secondVC", sender: self)
} else {
let alert = UIAlertView(title: "Warning!", message: self.message, delegate: nil, cancelButtonTitle: "OK")
alert.show()
}
}
}
task.resume()
}
else{
print("Internet connection FAILED")
let alert = UIAlertView(title: "No Internet Connection", message: "Make sure your device is connected to the internet.", delegate: nil, cancelButtonTitle: "OK")
alert.show()
}
First of all, I am adding indicator view for login screen and then, as soon as token came my Bool success variable is becoming true and I am dismissing the activity indicator then it needs to be gone next screen but it is not working right now and also I am getting following error.
Warning: Attempt to present UITabBarController on UIAlertController: whose view is not in the window hierarchy!
What could be the reason where am I doing wrong?
I think the error itself explains the issue here:
Attempt to present UITabBarController on UIAlertController: whose view is not in the window hierarchy!
As you are presenting the UIAlertController in this line of code :
present(alert, animated: true, completion: nil)
but you are not dismissing it and trying to performSegue on UIAlertController:
self.performSegue(withIdentifier: "secondVC", sender: self)
Dismiss the UIAlertController whenever you present it.
Refer this :
How to programmatically dismiss UIAlertController without any buttons?
When i run my code i get this error and i don't know why.
Error Domain=NSCocoaErrorDomain Code=3840 "No value." UserInfo={NSDebugDescription=No value.}
I looked for it on the internet but i didn't find something.
This is my code:
let myUrl = NSURL(string: "http://foodhelper.club/registerUser.php");
let request = NSMutableURLRequest(URL:myUrl!);
request.HTTPMethod = "POST";
let postString = "userEmail=\(userEmail!)&userFirstName=\(userFirstName!)&userLastName=\(userLastName!)&userPassword=\(userPassword!)";
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: { (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
dispatch_async(dispatch_get_main_queue())
{
if error != nil {
self.alertMessage(error!.localizedDescription)
print("fail")
return
}
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSDictionary
print ("1")
if let parseJSON = json {
let userId = parseJSON["userId"] as? String
print ("2")
if( userId != nil)
{
let myAlert = UIAlertController(title: "Alert", message: "Registration successful", preferredStyle: UIAlertControllerStyle.Alert);
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default){(action) in
self.navigationController?.popViewControllerAnimated(true) }
myAlert.addAction(okAction);
self.presentViewController(myAlert, animated: true, completion: nil)
} else {
let errorMessage = parseJSON["message"] as? String
print ("3")
if(errorMessage != nil)
{
self.alertMessage(errorMessage!)
}
}
}
} catch{
//email vergleich fehlt, egal ob
print(error)
print("catched error")
let myAlert = UIAlertController(title: "Alert", message: "Registration successful", preferredStyle: UIAlertControllerStyle.Alert);
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default){(action) in
self.navigationController?.popViewControllerAnimated(true)
}
myAlert.addAction(okAction);
self.presentViewController(myAlert, animated: true, completion: nil)
}
}
}).resume()
}
Thank you for your help
You need to set the content-type header value to use JSON.
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
Updated the code to Swift 3 and removed everything unrelated to the request:
let myUrl = URL(string: "http://foodhelper.club/registerUser.php");
var request = URLRequest(url:myUrl!);
request.httpMethod = "POST";
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let postString = "userEmail=email&userFirstName=firstname&userLastName=lastname&userPassword=password";
request.httpBody = postString.data(using: String.Encoding.utf8);
URLSession.shared.dataTask(with: request, completionHandler: { (data:Data?, response:URLResponse?, error:Error?) -> Void in
if error != nil {
print("fail")
return
}
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
print ("1")
if let parseJSON = json {
let userId = parseJSON["userId"] as? String
print ("2")
if( userId != nil) {
} else {
let errorMessage = parseJSON["message"] as? String
print ("3")
}
}
} catch{
print(error)
}
}).resume()
I just got same error, in my case this happened in Swift (toJson method) while converting empty Data (not NSData) to Json.
Solution, simply check if Data is empty first, like:
public typealias JsonValue = AnyHashable;
public typealias Json = [String: JsonValue];
extension Data {
public func toJson() throws -> Json {
if self.isEmpty {
return Json();
}
return try JSONSerialization.jsonObject(with: self, options: [])
as? Json ?? Json();
}
public static func fromJson(_ data: Json) throws -> Data {
return try JSONSerialization.data(withJSONObject: data);
}
}
I am a beginner in iOS development with Swift language. I have a JSON file contains the data as below.
{
"success": true,
"data": [
{
"type": 0,
"name": "Money Extension",
"bal": "72 $",
"Name": "LK_Mor",
"code": "LK_Mor",
"class": "0",
"withdraw": "300 $",
"initval": "1000 $"
},
{
},
{
},
]
}
I want to parse this file and have to return the dictionary which contain the data in the JSON file. This is the method I wrote.
enum JSONError: String, ErrorType {
case NoData = "ERROR: no data"
case ConversionFailed = "ERROR: conversion from JSON failed"
}
func jsonParserForDataUsage(urlForData:String)->NSDictionary{
var dicOfParsedData :NSDictionary!
print("json parser activated")
let urlPath = urlForData
let endpoint = NSURL(string: urlPath)
let request = NSMutableURLRequest(URL:endpoint!)
NSURLSession.sharedSession().dataTaskWithRequest(request) { (data, response, error) -> Void in
do {
guard let dat = data else {
throw JSONError.NoData
}
guard let dictionary: NSDictionary = try NSJSONSerialization.JSONObjectWithData(dat, options:.AllowFragments) as? NSDictionary else {
throw JSONError.ConversionFailed
}
print(dictionary)
dicOfParsedData = dictionary
} catch let error as JSONError {
print(error.rawValue)
} catch {
print(error)
}
}.resume()
return dicOfParsedData
}
When I modify this method to return a dictionary, it always return nil. How can I modify this method.
You can not return for an asynchronous task. You have to use a callback instead.
Add a callback like this one:
completion: (dictionary: NSDictionary) -> Void
to your parser method signature:
func jsonParserForDataUsage(urlForData: String, completion: (dictionary: NSDictionary) -> Void)
and call the completion where the data you want to "return" is available:
func jsonParserForDataUsage(urlForData: String, completion: (dictionary: NSDictionary) -> Void) {
print("json parser activated")
let urlPath = urlForData
guard let endpoint = NSURL(string: urlPath) else {
return
}
let request = NSMutableURLRequest(URL:endpoint)
NSURLSession.sharedSession().dataTaskWithRequest(request) { (data, response, error) -> Void in
do {
guard let dat = data else {
throw JSONError.NoData
}
guard let dictionary = try NSJSONSerialization.JSONObjectWithData(dat, options:.AllowFragments) as? NSDictionary else {
throw JSONError.ConversionFailed
}
completion(dictionary: dictionary)
} catch let error as JSONError {
print(error.rawValue)
} catch let error as NSError {
print(error.debugDescription)
}
}.resume()
}
Now you can use this method with a trailing closure to get the "returned" value:
jsonParserForDataUsage("http...") { (dictionary) in
print(dictionary)
}
this code is expected to do a Payout in PayPal using MassPay. It fails and I don't get a response from PayPal that tells me what is going on. Can somebody point me in the right direction?
let postsEndpoint: String = "https://api-3t.sandbox.paypal.com/nvp"
var postsUrlRequest = NSMutableURLRequest(URL: NSURL(string: postsEndpoint)!)
postsUrlRequest.HTTPMethod = "POST"
let paymentDict = ["USER" : "example.mydomain.com",
"PWD" : "PQDBVQXJYH4***",
"SIGNATURE" : "A9pEq0L3-2vjFoem1ajRi-b-0nWBAkujmPM.O5dJ9u-m7Vf***",
"METHOD" : "MassPay",
"VERSION" : "93",
"RECEIVERTYPE" : "EmailAddress",
"CURRENCYCODE" : "USD",
"EMAILSUBJECT" : "First payment test form swift",
"L_AMT0" : 1.25,
"L_EMAIL0" : "exampla#gmail.com",
"L_NOTE0" : "first test from swift",
"L_UNIQUEID0" : "KS1946-3"]
let newPost: NSDictionary = paymentDict
do {
let jsonPost = try NSJSONSerialization.dataWithJSONObject(newPost, options: [])
postsUrlRequest.HTTPBody = jsonPost
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let createTask = session.dataTaskWithRequest(postsUrlRequest, completionHandler: {
(data, response, error) in
guard let responseData = data else {
print("Error: did not receive data")
return
}
guard error == nil else {
})
createTask.resume()
} catch {
print("Error: cannot create JSON from post")
}
}
working code for MassPay post to PayPal with swift from my App
func payPalMassPayOutMessage() {
let postsEndpoint: String = "https://api-3t.sandbox.paypal.com/nvp"
let postsUrlRequest = NSMutableURLRequest(URL: NSURL(string: postsEndpoint)!)
postsUrlRequest.HTTPMethod = "POST"
let paymentString = "USER=alex***.mydomain&PWD=PQDBVQXJYH4****&SIGNATURE=A9pEq0L3-2vjFoem1ajRi-b-0nWBAkujmPM.O5dJ9u-m7VfmkDJg****&METHOD=MassPay&VERSION=93&RECEIVERTYPE=EmailAddress&CURRENCYCODE=USD&EMAILSUBJECT=First payment test form swift&L_AMT0=1.25&L_EMAIL0=lguerra10#gmail.com"
do {
postsUrlRequest.HTTPBody = paymentString.dataUsingEncoding(NSUTF8StringEncoding)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let createTask = session.dataTaskWithRequest(postsUrlRequest, completionHandler: {
(data, response, error) in
guard let responseData = data else {
print("Error: did not receive data")
return
}
guard error == nil else {
print("error calling post ")
print(error)
return
}
guard let dataSring = NSString(data: data!, encoding: NSUTF8StringEncoding) else {
print("error parse dataString ")
return
}
print("response data \(dataSring)")
})
createTask.resume()
} catch {
print("Error: cannot encode paymentString")
}
}