Clarifications on JSONDecoder when decoding a single value - swift

I was trying to perform some tests on JSONDecoder and I've encountered a strange behavior. In particular, when I use the following code an error is thrown.
let data = "Sample String".data(using: .utf8)!
do {
let decoder = JSONDecoder()
let decoded = try decoder.decode(String.self, from: data)
print(decoded)
} catch {
print(error)
}
dataCorrupted(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})))
On the contrary if I put a number as string and Int.self as the decoding type the value is printed correctly.
let data = "100".data(using: .utf8)!
do {
let decoder = JSONDecoder()
let decoded = try decoder.decode(Int.self, from: data)
print(decoded)
} catch {
print(error)
}
100
Any reason why this happens?

because some string is not valid json, but "some string" is.
you need quotes in your string:
let data = "\"Sample String\"".data(using: .utf8)!

Related

No value associated with key CodingKeys(stringValue: \"data\", intValue: nil) (\"data\")

If you wanna test Postman.. You can test on Postman. I couldn't decode data. How can I decode ?
Error:
keyNotFound(CodingKeys(stringValue: "data", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: "data", intValue: nil) ("data").", underlyingError: nil))
Model:
// MARK: - CountryResponse
struct CountryResponse: Codable {
let countryData: [CountryData]
enum CodingKeys: String, CodingKey {
case countryData = "data"
}
}
// MARK: - CountryData
struct CountryData: Codable {
let code: String
let currencyCodes: [String]
let name, wikiDataID: String
enum CodingKeys: String, CodingKey {
case code, currencyCodes, name
case wikiDataID = "wikiDataId"
}
}
Service:
class CountryService {
func getAllCountry() {
if let url = URL(string: "https://wft-geo-db.p.rapidapi.com/v1/geo/countries?limit=10") {
var request = URLRequest(url: url)
request.addValue("wft-geo-db.p.rapidapi.com", forHTTPHeaderField: "x-rapidapi-host")
request.addValue("api key", forHTTPHeaderField: "x-rapidapi-key")
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else { return }
do {
let response = try JSONDecoder().decode(CountryResponse.self, from: data)
print("response: \(response)")
} catch let error {
print("data decode edilemedi. \(error)")
}
}
task.resume()
} else {
print("hatalı url.")
}
}
}
This is not the answer to your question, but this is too big for comment, while I think it's important to explain your failure.
It's best to start response handling not from parsing JSON data, but from
Checking whether error is nil. If error is not nil, there's no point to continue with parsing
Check response to make sure response.statusCode is 2xx series (most commonly 200). If it's anything else (e.g. 4xx, 5xx), then the data will probably contain the error received from the server (or nothing at all), but will definitely not contain JSON you expect.
Apple has a good example here:
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
// handle error
return
}
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
// handle the error returned by a server
return
}
// now you are ready to look at the data
guard let data = data else { return }
// ...
I think your code will exit either in error or httpResponse condition, and that will explain to you what is failing. Also this is a better practice in general.
I received this error. Here is actual error from xcode console.
valueNotFound(Swift.String, Swift.DecodingError.Context(codingPath:
[CodingKeys(stringValue: "result", intValue: nil), _JSONKey(stringValue:
"Index 152", intValue: 152), CodingKeys(stringValue: "Field",
intValue: nil)], debugDescription: "Expected String value but found null
instead.", underlyingError: nil))
The error tells you which element of the array is missing the data and causing the error. In array "Index 152", the value of Field was null, it was a data entry issue. Maybe some of you will have a similar issue as well.
Swift will complain about null values like this when parsing results using a struct.

Get error description from caught error

I do something like this:
let decoder = JSONDecoder()
do
{
let decodedData = try decoder.decode(type, from: data)
}
catch DecodingError.dataCorrupted
{
let descr = ???
Log.error("Failed to decode JSON response. Error was: \(descr)")
}
how can I access the error description from this? Why can I not simply catch any kind of error in one catch and access its debug description?
How to access the error description
In Swift, a lot of the errors conform to the protocol LocalizedError, which will give you a variable localizedDescription: String? that you can use to print an error message. DecodingError should not be any different.
How to catch any kind of error
You should be able to catch any kind of errors in one catch. In order to do this, you can use
catch let error as DecodingError {
// Any error of type DecodingError
}
or
catch {
// Any possible error
}
Putting it all together
If I understand correctly, you are tring to catch any error of type DecodingError. In that case, you can simply do the following
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(type, from: data)
} catch let error as? DecodingError {
Log.error("Failed to decode JSON response. Error was: \(String(describing: error.localizedDescription))")
}

error when convert JSON to Dictionary Swift

can you help me,
I'm facing an issue if the JSON came with multilines like this
"{\"groupId\":\"58\",\"chat\":\"send 2lines\nsecondline\"}"
I'm taking the response from server and convert it with this function
let dataDic = self.convertToDictionary(text: (remoteMessage.appData["message"]! as AnyObject) as! String)
print(dataDic!)
and this is my function
func convertToDictionary(text: String) -> [String: AnyObject]? {
if let data = text.data(using: String.Encoding.utf8) {
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:AnyObject]
return json
} catch {
print(error.localizedDescription)
}
}
return nil
}
but the problem came if the code have multilines because it's put \n in the return and
it gives me
The data couldn’t be read because it isn’t in the correct format
Error Domain=NSCocoaErrorDomain Code=3840 "Unescaped control character around character 145." UserInfo={NSDebugDescription=Unescaped control character around character 145.}
You should put an extra "\" before "\n", before parsing your JSON. Try using "replacingOccurencesOf" function.
That way your JSON is formatted before parsing.

RSAUtils Decryption , Converting data to it's original value - Swift

i am trying to encrypt and decrypt a string using RSAUtils swift library, i am having a problem returning the decrypted text to it's original value . this is the code:
let PUBLIC_KEY = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJh+/sdLdlVVcM5V5/j/RbwM8SL++Sc3dMqMK1nP73XYKhvO63bxPkWwaY0kwcUU40+QducwjueVOzcPFvHf+fECAwEAAQ=="
let sampleText:String = "WHATS UP"
let encrypted:Data? = RSAUtils.encryptWithRSAPublicKey(sampleText.data(using: String.Encoding.utf8)!, pubkeyBase64: PUBLIC_KEY, keychainTag: "12345")!
let decrypted:Data? = RSAUtils.decryptWithRSAPublicKey(encrypted!, pubkeyBase64: PUBLIC_KEY, keychainTag: "12345")
let encryptedDataText = encrypted!.base64EncodedString(options: NSData.Base64EncodingOptions())
let decryptedDataText = decrypted!.base64EncodedString(options: NSData.Base64EncodingOptions())
I tried to convert the decrypted data to string using this code:
if let string = String(data: decrypted! , encoding: .utf16) {
print(string)
} else {
print("not a valid UTF-16 sequence")
}
But it prints "㺸ꉄ꤈꽹㲞㏯荘뼵鉉큅령嬰ꢤẲ毪쌶⏤ᱼ埡佒�ࡊᩏ⧚㨈؍੯屍" I also tried to decode the base64 value using :
let decodedData = Data(base64Encoded: decryptedDataText)!
let decodedString = String(data: decodedData, encoding: .utf8)!
print(decodedString)
It causes an error
Fatal error: Unexpectedly found nil while unwrapping an O
probably the text is not a valid base64 string.
How can i convert the decrypted data to it's original value.
Thanks.

Convert Dictionary to Base64: error Segmentation fault 11

I am trying to create JSON Web Token using JSONSerialization class, Swift 3 and Xcode 8.1, but my project fails to build with error:
Command failed due to signal: Segmentation fault 11.
Anyone knows why my code is not correct?
If I comment out this code from the project, the project builds.
let customerError = "Custom Error"
enum headerError: Error {
case customerError
}
let headerJWT: [Dictionary] = ["alg":"RS256","typ":"JWT"]
//Convert headerJWT to Data
do {
let headerJWTData: Data = try? JSONSerialization.data(withJSONObject:headerJWT,options: JSONSerialization.WritingOptions.prettyPrinted)
} catch headerError.customerError {
print("could not make data")
}
//Convert headerData to string utf8
do {
let headerJWTString = try String(data: headerJWTData,encoding:String.Encoding.utf8) as! String
} catch {
print("string could not be created")
}
//Convert headerJWTString to base64EncodedString
do {
let headerJWTBase64 = try Data(headerJWTString.utf8).base64EncodedString()
} catch {
"base64 could not be created"
}
Once you create the Data from using JSONSerialization, you simply use the method from Data to get a base64 encoded string.
let headerJWT: [Dictionary] = ["alg":"RS256","typ":"JWT"]
do {
let headerJWTData: Data = try? JSONSerialization.data(withJSONObject:headerJWT,options: JSONSerialization.WritingOptions.prettyPrinted)
let headerJWTBase64 = headerJWTData.base64EncodedString()
} catch headerError.customerError {
print("could not make data")
}
You can pass different options to base64EncodedString() depending on what format you need the base64 string to be in.