I have been stuck for the last few days on this and have read countless posts here on Stackoverflow and across the web, but am still a little lost. (by the way I am a bit of a newbie in Swift).
i have the following code
Alamofire.request(.GET, "url")
.response { (request, response, data, error) in
printIn(data)}
This prints out a long string of numbers in the Console, which is perfect, exactly what I need.
However I now would like to iterate through these and get the number at certain index's, so would like to convert this into a string or NSData.
I have tried many different ways but have not yet found how to do this, if somebody could please help me I would be very grateful.
I have tried using
Alamofire.request(.GET, "url")
.responseString(encoding: NSASCIIStringEncoding) { (request, response, data, error) -> Void in
println(data)
}
but this only prints out a jumbled up mess.
many thanks
Chris
You say:
However I now would like to iterate through these and get the number at certain index's, so would like to convert this into a string or NSData.
When you use response, the data parameter actually is a NSData. So just cast the variable to the appropriate type, and you should be in business, e.g.:
Alamofire.request(.GET, urlString)
.response { (request, response, data, error) in
if let data = data as? NSData {
for i in 0 ..< data.length {
var byte: UInt8!
data.getBytes(&byte, range: NSMakeRange(i, 1))
print(String(format: "%02x ", byte))
}
}
}
In my example loop, just logging the hex string representation of the byte variable, but it's a numeric value with which you can do whatever you'd like.
The data is NSData and ascii encoded (in your 2nd example)
let s = NSString(data: data, encoding: NSASCIIStringEncoding)
in the first case you don't specify an encoding and so it defaults to NSUTF8
let s = NSString(data: data, encoding: NSUTF8StringEncoding)
Related
I get a response from an API (unfortunately, I cannot change it) that looks something like (just an example):
As bytes => "{\"key\":\"value\"}"
The starting and ending quotes and the escaped quotes are all part of the response, I am solving it in a really ugly way that looks like this:
// (...) Receiving response
guard var responseString = String(bytes: data, encoding: .utf8) else {
print("Response wasn't just a string, great!") // unfortunately, this never happens
return
}
responseString = responseString.trimmingCharacters(in: .whitespacesAndNewlines) // make sure it is trimmed
responseString = String(responseString.dropFirst()) // drop the quote at the start
responseString = String(responseString.dropLast()) // drop the quote at the end
responseString = responseString.replacingOccurrences(of: "\\\"", with: "\"")// convert all \" to " (and hope nothing else is escaped <<< this is really bad!!!)
let responseDataToDecode = responseString.data(using: .utf8)!
// (...) decoding with JSONDecoder
Is there a way to automatically unescape the string and use the JSON object that is contained in it?
If it's double-encoded, then you just need to double-decode. If I understand correctly, the incoming data is like this:
let str = #""{\"key\":\"value\"}""#
// "{\\"key\\":\\"value\\"}"
The first byte is ", the second byte is {, the third byte is \, the fourth byte is ".
That's a JSON-encoded string. So decode that as a string (there was a time when this didn't work because it's a "fragment," but it works fine currently, at least in all my tests):
let decoder = JSONDecoder()
let string = try! decoder.decode(String.self, from: Data(str.utf8)) // {"key":"value"}
And then decode that as your type ([String:String] just for example):
let result = try! decoder.decode([String:String].self, from: Data(string.utf8))
// ["key": "value"]
(IMO this kind of double-encoding is fine, BTW, and I'm not sure why there are so many comments against it. Serializing an arbitrary object makes a lot more sense in many cases than forcing the schema to deal with an arbitrary structure. As long as it's cleanly encoded, I don't see any problem here.)
There's a first step: You need an official documented statement what exactly the format of your data is. It looks like someone took some data, turned it into JSON data, interpreted the data as a string, and then converted the string to a JSON fragment. It's not difficult to decode the JSON fragment, getting a string, turning the string into data, and decoding that data as JSON (starting with JSONSerialization and .allowFragments, probably the only time you should use .allowFragments in your life). Doing it without swearing is hard.
But first you want in writing that this is actually the format. Because I would bet that whoever is responsible for that data format will eventually change it without telling you and break your code.
I'm trying to perform a HTTP request to a server: the content is a JSON object, which contains a numeric value for the key "amount". If the "amount" is a value with a decimal digit, e.g. 1.6, the request will contain the value
1.6000000000000001, and this value is not accepted by the Server (the api is Java made and the type is a float . I cannot send a String to the server, since the API that receives the data from me can only accept numbers for the "amount". I tried to perform the request with Siesta Framework or with dataTask, but the result is always the same
this is how I create the request (I omitted the less important parts)
let jsonData = try! JSONSerialization.data(withJSONObject: jsonObject) // jsonObject contains the Double value "amount"
let request = URLRequest(url: url)
request.httpBody = jsonData
let task = URLSession.shared.dataTask(with: request)
task.resume()
Without code that fully reproduces the issue, it’s hard to say for sure, but I imagine what you’re seeing is this behavior:
let amount = 1.6 // Double
let jsonObject = ["amount": amount]
let jsonData = try! JSONSerialization.data(withJSONObject: jsonObject)
String(data: jsonData, encoding: String.Encoding.utf8)
Swift Foundation’s JSON serialization always formats numeric values to their full precision — so the double-precision number 1.6 gets formatted as 1.6000000000000001.
Solution 1: Send a string
You can and should send a string if the server accepts it:
let amount = "1.6" // String
let jsonObject = ["amount": amount]
let jsonData = try! JSONSerialization.data(withJSONObject: jsonObject)
String(data: jsonData, encoding: String.Encoding.utf8)
Note that a string is the only correct way to send this value if you are dealing with money or anything else where exact values matter: even if you spell it as 1.6, a standard JSON parser will likely convert it to a floating point on the receiving end.
Solution 2: Use Decimal to alter the formatting
If you just need to format it with less precision to make it pass validation on the server for some reason, you can embed it in the JSON as a Decimal instead of a Double and it will get formatted differently:
let amount = 1.6
let jsonObject = ["amount": Decimal(amount)]
let jsonData = try! JSONSerialization.data(withJSONObject: jsonObject)
String(data: jsonData, encoding: String.Encoding.utf8)
// {"amount":1.6}
You can even manipulate the Decimal to round to a certain precision.
Note, however, that this does not spare you from floating point precision issues: you are still sending a float according to the JSON spec, and it will still most likely be parsed as a float on the receiving end.
Without seeing your code it is challenging to offer help, however, one thing to check is whether you are using NumberFormatter to be sure you are getting the correct rounding on your values. Assuming you are pulling the number from a UITextField then you would need something like this:
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
if let textValue = textField.value {
var amount = numberFormatter.number(from: textValue)
}
A library save a token as Data in UserDefaults
//loading the token
if let token = UserDefaults.standard.object(forKey: "token") as? Data {
let base64Encoded = token.base64EncodedData
let base64EncodedString = token.base64EncodedString()
}
When I print that base64Encoded value in the console I get the value: Y2E2N2Y5NTItNDVkOC00YzZkLWFkZDMtZGRiMjc5NGE3YWI2OjdmZDU1ZTAyLWExMjEtNGQ1ZC05N2MzLWM5OWY4NTg5NTIzNg== as Data
When I copy this value from the output in the console and use this website https://www.base64decode.org/ I got the expected result. ca67f952-45d8-4c6d-add3-ddb2794a7ab6:7fd55e02-a121-4d5d-97c3-c99f85895236
My problem is that I don't get convert that base64Encoded to String in my code.
When I use base64EncodedString I got a string, but I can't figure out which format this is: PFsYdirs21247rRG/XcWOVGUGUcCCTzXz7VFAnunLJU= The method base64EncodedString() This is an method from Apple: https://developer.apple.com/documentation/foundation/nsdata/1413546-base64encodedstring
When I decode this value to utf8 at the website I got: <[v*]Fw9QG
When I try to convert my base64Encoded (Data, not String) to a string like this
if let toString = String(data: base64Encoded, encoding: String.Encoding.utf8) as String {
print("test \(toString)")
}
my compiler threw an error: Cannot convert value of type(Data.Base64EncodingOptions) -> Data to expected argument type Data
Here I found a solution to decode when my Data-value would be a string.
https://stackoverflow.com/a/31859383/4420355
So I'm quite confused about the results. Long story short:
I have base64Encoded Data (Y2E2N2Y5NTItNDVkOC00YzZkLWFkZDMtZGRiMjc5NGE3YWI2OjdmZDU1ZTAyLWExMjEtNGQ1ZC05N2MzLWM5OWY4NTg5NTIzNg==) and want them convert to an utf8 string. I don't get handle this.
Take this simplified example:
let dict: [String: AnyObject] = ["foo": ("bar" as NSString).dataUsingEncoding(NSUTF8StringEncoding)!9]
let json = try! NSJSONSerialization.dataWithJSONObject(dict, options: [])
I cannot get that to run, it crashes with:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (NSConcreteMutableData)'
I need to send a block of JSON to my server, where some values are strings and some are NSData. What am I doing wrong?
It's worth your time to read through the JSON RFC. Note:
JSON can represent four primitive types (strings, numbers, booleans, and null) and two structured types (objects and arrays).
JSON can't represent raw data, so you'll need to convert the NSData to one of those other types (typically a string). One common approach is:
let stringFromData = NSString(data: data, encoding: NSUTF8StringEncoding)
Just make sure you and your server agree on which encoding you'll use.
If you have a lot of these, you can map through your dictionary and convert them at once. There are a few possible implementations depending on your structure; here's one approach:
let dictWithString = dict.map { (key, value) -> (String, NSObject) in
if let value = value as? NSData {
return (key, NSString(data: value, encoding: NSUTF8StringEncoding)!)
}
return (key, value)
}
You can also convert your NSData into Base64 Encoded string in order to set to your json content.
let base64String = rawData.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
and decoding:
let rawData = NSData(base64EncodedString: base64String, options: .IgnoreUnknownCharacters)
What is wrong with this piece of code (which was inspired by this example)? It currently prints JSON string "(<5b5d>, 4)" instead of the expected "[]".
var tags: [[String]] = []
// tags to be added later ...
do {
let data = try NSJSONSerialization.dataWithJSONObject(tags, options: [])
let json = String(data: data, encoding: NSUTF8StringEncoding)
print("\(json)")
}
catch {
fatalError("\(error)")
}
Short answer: The creation of the JSON data is correct. The problem is in the
conversion of the data to a string, what you want is the NSString method:
let json = NSString(data: data, encoding: NSUTF8StringEncoding) as! String
which produces the expected [].
Slightly longer answer:
Your code
let json = String(data: data, encoding: NSUTF8StringEncoding)
calls the String init method
/// Initialize `self` with the textual representation of `instance`.
/// ...
init<T>(_ instance: T)
and the result is the textual representation of the tuple
(data: data, encoding: NSUTF8StringEncoding):
(<5b5d>, 4)
Actually you can call String() with arbitrary arguments
let s = String(foo: 1, bar: "baz")
print(s) // (1, "baz")
in Swift 2. This does not compile in Swift 1.2, so I am not sure if
this is intended or not. I have posted a question in the
Apple Developer Forums about that:
String init accepts arbitrary arguments.