I have some legacy code I am porting to Swift 2.0 which is producing an error, where previously it had worked ok.
The error is : Cast from 'NSData?' to unrelated type '[String : AnyObject]' always fails
Prior to moving the code to a new project I did not receive the error and the code had worked just fine.
Code:
func RestApiRequest(RestPath RestPath:String, Method:Alamofire.Method, Headers:NSDictionary?, Body:AnyObject?,ContentType:String,
OnCompletion:(StatusCode:Int, ResponseData:NSData, inout ResponseError:NSError?) -> (Void))
{
//Set REST url to be called
let mutableURLRequest = NSMutableURLRequest(URL: NSURL(string: "\(self.baseUrl!)\(RestPath)")!)
//Store body data if it exists
var data: NSData?
//Check if body value is set
if(Body != nil) {
//Check if body is a dictionary
if(Body is NSDictionary){
//Convert to JSON
let jsonBody = JSON(Body!)
data = try! jsonBody.rawData()
}
//Check if body is an array
else if(Body is NSArray) {
//convert to json
let jsonBody = JSON(Body!)
data = try! jsonBody.rawData()
}
else {
let bodyData = Body?.dataUsingEncoding(NSUTF8StringEncoding)
data = bodyData
}
}
let manager = Alamofire.Manager.sharedInstance
manager.session.configuration.HTTPAdditionalHeaders = Headers as? [NSObject : AnyObject]
manager.request(Method,mutableURLRequest, parameters: data as? [String : AnyObject], encoding: .Custom({ (convertible, params) in
let mutableRequest = convertible.URLRequest.copy() as! NSMutableURLRequest
if(data != nil) {
mutableRequest.HTTPBody = data
}
return (mutableRequest, nil)
})).response { (request, response, data, error) in
var _error = error
print(request)
print(NSString(data: data!, encoding:NSUTF8StringEncoding))
OnCompletion(StatusCode: (response?.statusCode)!, ResponseData: data!, ResponseError: &_error)
}
}
}
Is there a way to successfully make this conversion without producing an error?
Related
I don't find a way to parse a simple json object into a string object in swift. I have a network request which gives me this json response:
"\"asdf\""
When I try to parse this into a string in swift it looks like this:
"\"asdf\""
according this documentation from apple I should only need to do this:
Apple Swift documentation
let jsonValue = responseData as? String
But that does not work for me.
I need just asdf as string value.
Can anyone help me out?
Thanks in advance.
EDIT:
Here is my network request code:
let stringUrl = "https://test.fangkarte.de/v1.3/test"
let url = URL(string: stringUrl)!
let request = URLRequest(url: url)
let session = URLSession(configuration: URLSessionConfiguration.default)
let task = session.dataTask(with: request, completionHandler: {(data, response, error) -> Void in
if let data = data {
let json = String(data: data, encoding: String.Encoding.utf8)
let response = response as! HTTPURLResponse
if 200...299 ~= response.statusCode {
callback(true, response.statusCode, json!)
} else {
callback(false, response.statusCode, json!)
}
}
})
task.resume()
The value of the variable json is "\"testString\"" and not "testString"
You could try something like:
func parseJSON(_ data: Data) -> [String: Any]? {
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
let body = json["data"] as? [String: Any] {
return body
}
} catch {
print("Error deserializing JSON: \n\(error)")
return nil
}
return nil
}
To use:
let data = <variable holding JSON>.data(using: .utf8)
let jsonResult = parseJSON(data)
You get a json string so you can try
let jsonstring = "\"asdf\""
let data = jsonstring.data(using: .utf8)
do {
if let str = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as? String {
print(str)
}
}
catch let caught as NSError
{
}
I am calling a POST API but the post values are not getting recognised.
I am using alamofire for API handling
public func POSTRequest(postData : AnyObject?,APIName : String?,headers: HTTPHeaders?,success:#escaping (JSON) -> Void, failure:#escaping (String,String) -> Void)
{
let url = URL(string: APIName!)!
var urlRequest = URLRequest(url: url)
urlRequest.httpBody = postData as? Data
urlRequest.httpMethod = "POST"
urlRequest.allHTTPHeaderFields = headers
let manager = Alamofire.SessionManager.default
manager.session.configuration.timeoutIntervalForRequest = 30.0
manager.request(urlRequest).responseJSON { ( responseData) -> Void in
if responseData.result.isSuccess {
let statusCode = responseData.response?.statusCode
if(statusCode == 200){
let resJson = JSON(responseData.result.value!)
success(resJson)
}
else {
let resJson = JSON(responseData.result.value!)
let errorMsg = resJson["message"].stringValue
let errorTitle = resJson["title"].stringValue
failure(errorMsg,errorTitle)
}
}
if responseData.result.isFailure {
let error : Error = responseData.result.error!
failure(error.localizedDescription,"")
}
}
}
// MARK: Login
func userLogin(postData: AnyObject, completionHandler:#escaping (JSON) -> Void,failure:#escaping (String,String) -> Void)
{
let apiName = GlobalConstants.rootUrl + GlobalConstants.loginUrl
let httpHeaders: HTTPHeaders = getHeaders()
ApiRequestHandler.shared.POSTRequest(postData: postData, APIName: apiName, headers: httpHeaders, success: { (JSON) in
print("JSON \(JSON)")
completionHandler(JSON)
})
{ (Errormsg,ErrorTitle) in
print("Error \(Errormsg)")
failure(Errormsg,ErrorTitle)
}
}
I am creating the post data here
let postDataDct = NSMutableDictionary()
postDataDct.setValue(self.userNameTextField.text, forKey: "username")
postDataDct.setValue(self.phoneNumberTextField.text, forKey: "mobile")
guard let data = try? JSONSerialization.data(withJSONObject:
postDataDct , options: JSONSerialization.WritingOptions.prettyPrinted)
as NSData else {
return;
}
print(NSString(data: data as Data, encoding:
String.Encoding.utf8.rawValue)! as String)
header creation here
func getHeaders() -> HTTPHeaders {
let headers: HTTPHeaders = ["Content-Type": "application/json" ,"Authorization" : "Basic dGFuaXNocWdfgaFF%waXJvb3Q6dzBEMFlkSnR0"]
return headers
}
I am calling the login method here
ApiManager.shared.userLogin(postData: postDataDct as AnyObject, completionHandler: { (JSON) in
print(JSON)
})
{ (ErrorMsg, ErrorTitle) in
print(ErrorMsg)
}
Please help me to find the issue.
API is giving me error response, saying user name or password is empty.
For Posting Alamofire have its own Parameters Class Try to use that like this with make share using the exact keys as on server.
let parameter: Parameters = [
"Email": txtfld_email.text!,
"Password": txtfld_password.text!
]
Cross check your keys:
let postDataDct = NSMutableDictionary()
postDataDct.setValue(self.userNameTextField.text, forKey: "username")
postDataDct.setValue(self.phoneNumberTextField.text, forKey: "mobile")
You are making the request with username and mobile It seems like the keys will be username and password according to the response you are getting name or password is empty.
let postDataDct = NSMutableDictionary()
postDataDct.setValue(self.userNameTextField.text, forKey: "username")
postDataDct.setValue(self.phoneNumberTextField.text, forKey: "password")
Suggestion:
Don't use NSMutableDictionary in Swift, use its native Swift
counterpart, Dictionary
Have been researching on the parsing for quite a bit. With plethora of information avilable for JSON nothing seems to explain how to do in a sensible way to extract information with swift 3.
This is what got so far
func getBookDetails() {
let scriptUrl = "https://www.googleapis.com/books/v1/volumes?q=isbn:9781451648546" .
let myurl = URL(string:scriptUrl)
let request = NSMutableURLRequest(url: myurl!)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: myurl! ) { (data, response, error) in
if error != nil{
print("THIS ERROR",error!)
return
} else{
if let mydata = data{
do{
let myJson = try (JSONSerialization.jsonObject(with: mydata, options: JSONSerialization.ReadingOptions.mutableContainers)) as AnyObject
// print("this is the MY JSON",myJson) ---> prints out the json
if let dictonary = myJson["items"] as AnyObject? {
print("the DICTONARY",dictonary) // ----> OUTPUT
if let dictonaryAA = dictonary["accessInfo"] as AnyObject? {
print("the accessInfo",dictonaryAA)
}
}
} catch{
print("this is the in CATCH")
}
} //data
}
}
task.resume()
}
}
OUTPUT :
the DICTONARY (
{
accessInfo = {
accessViewStatus = SAMPLE;
country = US;
=============
RELEVANT DATA as in https://www.googleapis.com/books/v1/volumes?
q=isbn:9781451648546"
==========================
title = "Steve Jobs";
};
}
)
Just need to parse through the json data to get the name, author and title of the book with reference to isbn.
Know there should be a better way to do things that is easily understandable to someone new into the language
You can parse the api in two ways
Using URLSession:
let rawDataStr: NSString = "data={\"mobile\":\"9420....6\",\"password\":\"56147180..1\",\"page_no\":\"1\"}"
self.parsePostAPIWithParam(apiName: "get_posts", paramStr: rawDataStr){ ResDictionary in
// let statusVal = ResDictionary["status"] as? String
self.postsDict = (ResDictionary["posts"] as! NSArray!) as! [Any]
print("\n posts count:",self.postsDict.count)
}
func parsePostAPIWithParam(apiName:NSString, paramStr:NSString,callback: #escaping ((NSDictionary) -> ())) {
var convertedJsonDictResponse:NSDictionary!
let dataStr: NSString = paramStr
let postData = NSMutableData(data: dataStr.data(using: String.Encoding.utf8.rawValue)!)
let request = NSMutableURLRequest(url: NSURL(string: "http://13.12..205.248/get_posts/")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = nil
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error as Any)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse as Any)
do{
if let convertedJsonIntoDict = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary {
convertedJsonDictResponse = convertedJsonIntoDict.object(forKey: apiName) as? NSDictionary
// callback for response
callback(convertedJsonDictResponse)
}
} catch let error as NSError {
print(error)
}
}
Using Alamofire
func AlamofirePOSTRequest() {
let urlString = "http://13.12..205.../get_posts/"
let para = ["data": "{\"mobile\":\"9420....6\",\"password\":\"56147180..1\",\"page_no\":\"1\"}"]
Alamofire.request(urlString, method: .post, parameters: para , headers: nil).responseJSON {
response in
switch response.result {
case .success:
print("response: ",response)
let swiftyJsonVar = JSON(response.result.value!)
if let resData = swiftyJsonVar["posts"].arrayObject {
self.postsDict = resData as! [[String:AnyObject]]
}
print("\n \n alomafire swiftyJsonVar: ",swiftyJsonVar)
break
case .failure(let error):
print(error)
}
}
}
})
dataTask.resume()
}
First of all, all JSON types are value types in Swift 3 so the most unspecified type is Any, not AnyObject.
Second of all, there are only two collection types in the JSON type set, dictionary ([String:Any]) and array ([Any], but in most cases [[String:Any]]). It's never just Any nor AnyObject.
Third of all, the given JSON does not contain a key name.
For convenience let's use a type alias for a JSON dictionary:
typealias JSONDictionary = [String:Any]
The root object is a dictionary, in the dictionary there is an array of dictionaries for key items. And pass no options, .mutableContainers is nonsense in Swift.
guard let myJson = try JSONSerialization.jsonObject(with: mydata) as? JSONDictionary,
let items = myJson["items"] as? [JSONDictionary] else { return }
Iterate through the array and extract the values for title and authors which is an array by the way. Both values are in another dictionary for key volumeInfo.
for item in items {
if let volumeInfo = item["volumeInfo"] as? JSONDictionary {
let title = volumeInfo["title"] as? String
let authors = volumeInfo["authors"] as? [String]
print(title ?? "no title", authors ?? "no authors")
The ISBN information is in an array for key industryIdentifiers
if let industryIdentifiers = volumeInfo["industryIdentifiers"] as? [JSONDictionary] {
for identifier in industryIdentifiers {
let type = identifier["type"] as! String
let isbn = identifier["identifier"] as! String
print(type, isbn)
}
}
}
}
You are doing wrong in this line
if let dictonaryAA = dictonary["accessInfo"] as AnyObject?
because dictonary here is an array not dictionary. It is array of dictionaries. So as to get first object from that array first use dictonary[0], then use accessInfo key from this.
I am attaching the code for your do block
do{
let myJson = try (JSONSerialization.jsonObject(with: mydata, options: JSONSerialization.ReadingOptions.mutableContainers)) as AnyObject
// print("this is the MY JSON",myJson) ---> prints out the json
if let array = myJson["items"] as AnyObject? {
print("the array",array) // ----> OUTPUT
let dict = array.object(at: 0) as AnyObject//Master Json
let accessInf = dict.object(forKey: "accessInfo") //Your access info json
print("the accessInfo",accessInf)
}
}
Hope this helps you.
I'm trying to get a json from a server and deserialize it, but I try probelas with unescaped control characters.
My code is as follows ...
let urlFinal = "http://000.0000.000.000:8080"
let jsonUrl = urlFinal
let session = NSURLSession.sharedSession()
let shotsUrl = NSURL(string: jsonUrl)
let task = session.dataTaskWithURL(shotsUrl!) {data, response, error in
guard data != nil else {
falha()
return
}
//let json = JSON(data: data!)
//print(json["ServicoCliente"][0]["id"])
do {
let jsonData = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers )
let J = jsonData as! NSDictionary
print(jsonData)
let us = J["ServicoCliente"]
print(us)
dispatch_async(dispatch_get_main_queue(),{
sucesso()
});
} catch _ {
falha()
}
}
task.resume()
and I also tried this using Alamofire 3.0:
Alamofire.request(.GET, "http://000.000.000.000/", parameters: nil)
.responseJSON { response in
debugPrint(response) // prints detailed description of all response properties
print(response.request) // original URL request
print(response.response) // URL response
print(response.data) // server data
print(response.result) // result of response serialization
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
}
And get this error:
Unescaped control character around character 263
How can I remove characters without escape?
I use X-Code 7.3.1 and Swift 2.3
UPDATE:
Json
{"ServicoCliente":[{"id":"195","cliente":"247","enderecoFavoritos":"48","servicoProfissional":"194","ind_estado_cliente":"A","ind_estado_profissional":"","profissional_id":"240","profissional_nome":"PetMax","servicotipo_nome":"Petshop","servicosubtipo_nome":"Tosa ","dta_inc_alt":"2016-11-05 22:56:19.333","ind_finalizado":"N"}]}
To fix this, you must convert the data for string, remove characters and then convert to dataonly then deserialize
let urlFinal = "http://000.0000.000.000:8080"
let jsonUrl = urlFinal
let session = NSURLSession.sharedSession()
let shotsUrl = NSURL(string: jsonUrl)
let task = session.dataTaskWithURL(shotsUrl!) {data, response, error in
guard data != nil else {
falha()
return
}
var dataToString = String(data: data!, encoding: NSUTF8StringEncoding)
dataToString = stringByRemovingControlCharacters2(dataToString!)
let ndata = dataToString!.dataUsingEncoding(NSUTF8StringEncoding)
do {
let jsonData = try NSJSONSerialization.JSONObjectWithData(ndata!, options: NSJSONReadingOptions.MutableContainers )
let J = jsonData as! NSDictionary
print(jsonData)
let us = J["ServicoCliente"]
print(us)
dispatch_async(dispatch_get_main_queue(),{
sucesso()
});
} catch _ {
falha()
}
}
task.resume()
and add the function
func stringByRemovingControlCharacters2(string: String) -> String {
let controlChars = NSCharacterSet.controlCharacterSet()
var range = string.rangeOfCharacterFromSet(controlChars)
var mutable = string
while let removeRange = range {
mutable.removeRange(removeRange)
range = mutable.rangeOfCharacterFromSet(controlChars)
}
return mutable
}
Swift 5
func string(byRemovingControlCharacters inputString: String) -> String {
let controlChars = CharacterSet.controlCharacters
var range = (inputString as NSString).rangeOfCharacter(from: controlChars)
if range.location != NSNotFound {
var mutable = inputString
while range.location != NSNotFound {
if let subRange = Range<String.Index>(range, in: mutable) { mutable.removeSubrange(subRange) }
range = (mutable as NSString).rangeOfCharacter(from: controlChars)
}
return mutable
}
return inputString
}
I'm getting an issue while sending an HTTP GET command to a server to retrieve JSONObjects.
here is the command:
if let url: NSURL = NSURL(string: "http://11.22.33.44:8080/SRV/getAllUsers?owner=\(User.sharedInstance.email)") {
print("\nSending URL: \(url)")
let request = NSURLRequest(URL: url)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{
(response: NSURLResponse?, data: NSData?, error: NSError?)-> Void in
print("Response: \(response) \n")
do{
var datastring = NSString(data:data!, encoding:NSUTF8StringEncoding) as String?
print("Data: \(datastring)")
let json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments)
print("Json: \(json)")
}catch {
print("Error with Json: \(error)")
}
});
I received a HTTP 200 with all header information, but I'm trying to print NSData as String (nil) and also trying to retrieve my JSONObjects, I get the following message:
Data: nil
Error with Json: Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.}
I'm managing the server part, the servlet which is preparing JSONObjects is doing the following:
ObjectOutputStream objOut = new ObjectOutputStream(response.getOutputStream());
JSONObject jsonObj = new JSONObject();
jsonObj.put("id","1234");
objOut.writeObject(jsonObj);
objout.flush();
jsonObj = new JSONObject();
jsonObj.put("id","5678");
objOut.writeObject(jsonObj);
objout.flush();
and a last important information, I'm able to retrieve those JSONObject from an Android application without any problem but it seems the format expected in swift for JSONObjects are not the same...
EDIT
I changed the way to send HTTP GET command by using NSURLSession but before treating the JSONObject, I'm trying at least to display data as String:
typealias Payload = [String: AnyObject]
let url = NSURL(string: "http://11.22.33.44:8080/SRV/getAllUsers?owner=\(User.sharedInstance.email)")
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url!) { (data, response, error) -> Void in
if(error == nil)
{
let err : NSError! = nil
print("Response \(response)")
guard let _:NSData = data, let _:NSURLResponse = response where error == nil else {
print("error")
return
}
let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("Data: \(dataString)")
var json: Payload!
// 1
do {
json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions()) as? Payload
print("JSon: \(json)")
} catch {
print(error)
//XCPlaygroundPage.currentPage.finishExecution()
}
}
else
{
print(error?.description)
}
}
task.resume()
I Received a HTTP 200 with the right header information, so I'm sure the servlet is successfully called, but get nil in data
Data: nil
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.}
EDIT #2
by using the following line
guard let _:NSData = data, let _:NSURLResponse = response where error == nil else {
print("error data")
return
}
print("data: \(data?.debugDescription)")
I can retrieve the following:
data: Optional("OS_dispatch_data: data[0x7fc68bc45380] = { composite, size = >1067, num_records = 5 record[0] = { from = 0, length = 404, data_object = >0x7fc68bc999d0 }, record[1] = { from = 0, length = 135, data_object = >0x7fc68be1f830 }, record[2] = { from = 0, length = 264, data_object = >0x7fc68bc99a80 }, record[3] = { from = 0, length = 133, data_object = >0x7fc68bf97c20 }, record[4] = { from = 0, length = 131, data_object = >0x7fc68bca0b40 }, }>")
means I am able to retrieve data (at least!) but I don't know how I can extract from this data my JSONObject....
SOLUTION
I finally found my problem.
server part needs to prepare JSONObject like this:
response.setContentType("application/json");
// Get the printwriter object from response to write the required json object to the output stream
PrintWriter out = response.getWriter();
// Assuming your json object is **jsonObject**, perform the following, it will return your json object
out.print(jsonObject);
out.flush();
instead of using ObjectOutPutStream.
Swift part can retrieve it like this:
let url_to_request = "http://11.22.33.44:8080/SRV/getAllUsers?owner=\(User.sharedInstance.email)"
let url:NSURL = NSURL(string: url_to_request)!
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "GET"
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData
request.timeoutInterval = 10
let task = session.dataTaskWithRequest(request) {
(
let data, let response, let error) in
guard let _:NSData = data, let _:NSURLResponse = response where error == nil else {
print("error data")
return
}
var json: AnyObject?
do
{
json = try NSJSONSerialization.JSONObjectWithData(data, options: [])
for anItem in json as! [Dictionary<String, AnyObject>] {
let nom = anItem["nom"] as! String
let prenom = anItem["prenom"] as! String
let id = anItem["id"] as! String
print("nom: \(nom) prenom: \(prenom) id: \(id)")
}
}
catch
{
print("error Serialization")
return
}
}
task.resume()