Swift Unable To Convert NSString To JSON - ios

I am using a WKUserScript for communications between a UIWebView and a server. This particular code will allow user's to search for geographical places. I receive a message back just fine in the function
func userContentController(userContentController: WKUserContentController,
didReceiveScriptMessage message: WKScriptMessage)
The variable message has an AnyObject body. That value I want to convert to a JSONObject so I can access it's contents. Here is the message.body:
{
"status":"OK",
"predictions":[
{
"description":"Dallas, TX, United States",
"id":"fa589a36153613fc17b0ebaebbea7c1e31ca62f0",
"matched_substrings":[{"length":6,"offset":0}],
"place_id":"ChIJS5dFe_cZTIYRj2dH9qSb7Lk",
"reference":"CkQxAAAAJNbPZRkdsyxuKT4FzFmgpBx9HWnZLNhxprRQB0zy62sHCXo3tkHfV_M5dK4Cabp2KL43nIKAAyrv_RI4qbvNfRIQ1dzEGuqywMIAlNg_1AKvoRoUQN32C2uNo4KzZ9j58lB-wjPpjJw",
"terms":[
{"offset":0,"value":"Dallas"},
{"offset":8,"value":"TX"},
{"offset":12,"value":"United States"}
],
"types":["locality","political","geocode"]},
{
"description":"Dallas Athletic Club Drive, Dallas, TX, United States",
"id":"37c4f8d416b9d3975ad57662eb022a0d410e8f76",
"matched_substrings":[{"length":6,"offset":0}],
"place_id":"EjVEYWxsYXMgQXRobGV0aWMgQ2x1YiBEcml2ZSwgRGFsbGFzLCBUWCwgVW5pdGVkIFN0YXRlcw",
"reference":"CkQ5AAAArHSWkIVO6uTH4qE6LxRHshWAfgSnMfxXiBxqf_ZO3O-xQ8RIKKHA9QT7LKwf6Ic788Bzy_I2FpemvcQhE6o5ZRIQ5td4XsjIiyX6D6_dgI3YIxoURu_oROPuOguuorK3Tw11veN7XJI",
"terms":[
{"offset":0,"value":"Dallas Athletic Club Drive"},
{"offset":28,"value":"Dallas"},
{"offset":36,"value":"TX"},
{"offset":40,"value":"United States"}
],
"types":["route","geocode"]
}
]
}
The JSONObject has a status, which lets me know if the results are valid or if an error occurred. I am using SwiftyJSON to create and access my JSONs. I create the JSONObject:
let json = JSON(message.body as! NSString)
and I attempt to access the status key like:
if let status = json["status"].string {
print("status: \(status)")
}
But I am no able to reach the print statement. I have noticed that NSDictionary and JSON have new line characters when you print them out to the console, but I didn't think that would make a difference. Does anyone know why I am unable to retrieve the status variable from the JSON?

You can't initalize a SwiftyJSON object directly with a string, you have to convert this string to data (or use the data you got in the first place if that's the case).
Assuming message.body is a string:
if let dataFromString = message.body.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) {
let json = JSON(data: dataFromString)
if let status = json["status"].string {
print("status: \(status)")
}
}

Related

Xcode, The given data was not valid JSON, Can't read the JSON from API properly

Hello I am creating an app with Xcode and I am having the following problem, I created this API (if you enter the link you'll see the JSON data) https://proyecto-idts6.epizy.com/models/getCategorias.php
If you dont want to enter the link here is how this si how the structure of the JSON looks like:
{
"items":[
{
"categorie":"Fruits",
"id_categorie":"1"
},
{
"categorie":"Animals",
"id_categorie":"2"
},
{
"categorie":"Juices",
"id_categorie":"3"
},
{
"categorie":"Vegetables",
"id_categorie":"4"
},
{
"categorie":"Alcohol",
"id_categorie":"5"
},
{
"categorie":"Desserts",
"id_categorie":"6"
}
]
}
The problem I have is that when I try to decode the data from the API it cant't be decoded properly, I am trying to recreate the same code of this youtube video, but with my API: https://www.youtube.com/watch?v=sqo844saoC4
What I want basically is to print the categories and storage each of them in variables (because i'll need to move the variables between screens)
This is how my code looks like:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let url = "https://proyecto-idts6.epizy.com/models/getCategorias.php"
getData(from: url)
//Here is where i want to storage the variables from the JSON
}
private func getData(from url: String) {
let task = URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: { data, response, error in
guard let data = data, error == nil else {
print("something went wrong")
return
}
do {
let result = try JSONDecoder().decode([ResultItem].self, from: data)
print(result)
}
catch {
print("failed to convert\(error)")
}
})
task.resume()
}
}
struct Response: Codable {
let items: [ResultItem]
}
struct ResultItem: Codable {
let categorie: String
}
My goal is to have variables for example like this: categorie1=("the category 1 called from the JSON"), categorie2=("the category 2 called from the JSON"), categorie3=("the category 3 called from the JSON"),...
The problem is not in the decoding but in the remote API.
Your endpoint (https://proyecto-idts6.epizy.com/models/getCategorias.php) instead of returning a JSON is returning the following HTML
<html><body><script type="text/javascript" src="/aes.js" ></script><script>function toNumbers(d){var e=[];d.replace(/(..)/g,function(d){e.push(parseInt(d,16))});return e}function toHex(){for(var d=[],d=1==arguments.length&&arguments[0].constructor==Array?arguments[0]:arguments,e="",f=0;f<d.length;f++)e+=(16>d[f]?"0":"")+d[f].toString(16);return e.toLowerCase()}var a=toNumbers("f655ba9d09a112d4968c63579db590b4"),b=toNumbers("98344c2eee86c3994890592585b49f80"),c=toNumbers("f5490e280a5e50f74932909856c3d3a3");document.cookie="__test="+toHex(slowAES.decrypt(c,2,a,b))+"; expires=Thu, 31-Dec-37 23:55:55 GMT; path=/"; location.href="https://proyecto-idts6.epizy.com/models/getCategorias.php?i=1";</script><noscript>This site requires Javascript to work, please enable Javascript in your browser or use a browser with Javascript support</noscript></body></html>
So you are trying to decode that HTML content, which clearly leads to the error your reported
failed to convertdataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around line 1, column 0." UserInfo={NSDebugDescription=Invalid value around line 1, column 0., NSJSONSerializationErrorIndex=0})))

How could I "force" all properties in a struct? To be able to send them to an API?

I want to know how can I force all the properties from this struct to be able to send a POST request to our API?
First of all. I need all those optional properties because I make a GET request, I receive all those documents, I process the data, I add the file property (which is an object) then I need to send all those documents back to our server with a file added.
We have our Document
struct Document: Codable {
let allowedFormats: [String]?
let processWhereApply: [String]?
let isRequired: Bool?
let id, key, title, description: String?
var file: File?
// More properties
}
But it fails every time, because I'm not sending a string for example. I'm sending an Optional<String>
Is there anyway possible I can "force" all those properties to send them back? without like writting 25 lines of guard let var = var else { return } ?
I'm sending the POST request with the parameters like this
let params = [
"userId": userId,
"groupId": groupId,
"fileDocuments": documents! //sends all properties optional
] as [String: Any]
Api().saveDocuments(params: params)
I'm assuming that you are sending the data back as Json. In that case just use the Json encode method to convert the struct to Json which can be sent as a POST request. Json encode will deal with the null value issue by setting a corresponding value to the key in your json if the value exists and not creating the key if it doesn't exist.
For example just to get the json:
let doc1 = Document(!here you will be initialising variables!)
// below gives you json as data
let json = try! JSONEncoder().encode(doc1)
// if you want your json as string
let str = String(decoding: json, as: UTF8.self)
Here is an example making an alamofire POST request. In the case of alamofire it automatically encodes your struct as long as it conforms to Codable:
let doc1 = Document(!here you will be initialising variables!)
AF.request("https://yoururl.com",method: .post,parameters: doc1, encoder: JSONParameterEncoder.default).responseJSON { response in
switch response.result {
case .success(let json):
print("good response")
break
case .failure(let error):
print("bad response"
}
}

use of "po" to get data from server response in console log

i know this is very basic question that i am gonna ask but i need to ask how can i access data which is dictionary that is getting from server.
here is my response
JSON: {
message = "The email must be a valid email address.";}
now i want to do "po" in console log so what to write after in po statement
Thanks
All you need type
po yourResponseAsDictionary["message"]
UPDATED
Sorry I was thinking you already converted it.
If you are not using anything like SwiftyJSON or ObjectMapper to parse your json data then you can do just the following.
But I would recommend to use some lib to convert response directly to your model
let yourResponseFromNetwork = "{ \"country_code\" : \"+9\", \"user_id\" : 123456}"
if let data = yourResponseFromNetwork.data(using: String.Encoding.utf8) {
do {
if let dic = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:Any] {
let countryCode = dic["country_code"]
let userId = dic["user_id"]
}
} catch {
print("Error occurred")
}
}

JSON dictionary to JSON object in SwiftyJSON

On login, my server returns a user json object if the login was successful. The structure of JSON is as follows:
{
"user" = {
[
user info here
]
}
}
As you can see, it returns the user data inside an array, and unfortunately, this cannot be changed.
I am trying to parse this data in the most efficient way possible with SwiftyJSON, but I've run into a problem. I want to check if there is a "user" key, and if so, parse the JSON object as JSON rather than a dictionary [String: JSON]. However, I can't figure out how to just cast response["user"][0] as a JSON object rather than a dictionary.
if let userInfo = response["user"][0].dictionary {
let user = User.json_decode(userInfo) as! User
User.setUser(user)
success(user: user)
return
}
else {
failure(error: "Username or password was incorrect. Please try again")
}
try this, userinfo is supposed to contain the dictionary
if let user = response["user"].array, userinfo = user[0].dictionary {
println(userinfo)
} else {
failure(error: "Username or password was incorrect. Please try again")
}

How can I get the Swift/Xcode console to show Chinese characters instead of unicode?

I am using this code:
import SwiftHTTP
var request = HTTPTask()
var params = ["text": "这是中文测试"]
request.requestSerializer.headers["X-Mashape-Key"] = "jhzbBPIPLImsh26lfMU4Inpx7kUPp1lzNbijsncZYowlZdAfAD"
request.responseSerializer = JSONResponseSerializer()
request.POST("https://textanalysis.p.mashape.com/segmenter", parameters: params, success: {(response: HTTPResponse) in if let json: AnyObject = response.responseObject { println("\(json)") } },failure: {(error: NSError, response: HTTPResponse?) in println("\(error)") })
The Xcode console shows this as a response:
{
result = "\U8fd9 \U662f \U4e2d\U6587 \U6d4b\U8bd5";
}
Is it possible to get the console to show the following?:
{
result = "这 是 中文 分词 测试"
}
If so, what do I need to do to make it happen?
Thanks.
Instead of
println(json)
use
println((json as NSDictionary)["result"]!)
This will print the correct Chinese result.
Reason: the first print will call the debug description for NSDictionary which escapes not only Chinese chars.
Your function actually prints the response object, or more accurately a description thereof. The response object is a dictionary and the unicode characters are encoded with \Uxxxx in their descriptions.
See question: NSJSONSerialization and Unicode, won't play nicely together
To access the result string, you could do the following:
if let json: AnyObject = response.responseObject {
println("\(json)") // your initial println statement
if let jsond = json as? Dictionary<String,AnyObject> {
if let result = jsond["result"] as? String {
println("result = \(result)")
}
}
}
The second println statement will print the actual string and this code actually yields:
{
result = "\U8fd9 \U662f \U4e2d\U6587 \U6d4b\U8bd5";
}
result = 这 是 中文 测试

Resources