Generating a JSON-Array and converting it to a Base64-String - arrays

I'm trying to create a Base64-String in Swift. I have an example of a Base64-encoded string and its array-counterpart. My problem now is, that I don't know how I get an equivalent array to the one which is given in the example.
Because I didn't want to mess around in my XCode-project I did the following in a playground.
given array:
{"WHERE":{"Class":"%3f","Location":"3b"},"ORDER":["Day ASC","Location DESC"]}
given Base64-string:
eyJXSEVSRSI6eyJDbGFzcyI6IiUzZiIsIkxvY2F0aW9uIjoiM2IifSwiT1JERVIiOlsiRGF5IEFTQyIsIkxvY2F0aW9uIERFU0MiXX0=
First I'm decoding the example-string
let str = "eyJXSEVSRSI6eyJDbGFzcyI6IiUzZiIsIkxvY2F0aW9uIjoiM2IifSwiT1JERVIiOlsiRGF5IEFTQyIsIkxvY2F0aW9uIERFU0MiXX0="
let data = NSData(base64EncodedString: str, options: NSDataBase64DecodingOptions(rawValue: 0))
do {
let result = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
} catch let error {
print(error)
}
//"result" is ["WHERE": ["Class": "%3f", "Location": "3b"], "ORDER": ["Day ASC", "Location DESC"]]
Below I'm trying to reproduce the string from above
var array = [String : AnyObject]()
var arrayPartA = [String : String]()
arrayPartA["Class"] = "%3f"
arrayPartA["Location"] = "3b"
array["ORDER"] = ["Day ASC", "Location DESC"]
array["WHERE"] = arrayPartA
array //The playground says that "array" is ["ORDER": ["Day ASC", "Location DESC"], "WHERE": ["Class": "%3f", "Location": "3b"]]
//"ORDER" and "WHERE" are switched but I don't get them to be at the right position
let utf8str2: NSData = String(array).dataUsingEncoding(NSUTF8StringEncoding)!
let encodedStr = utf8str2.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
//Here "encodedStr" is WyJPUkRFUiI6ICgKICAgICJEYXkgQVNDIiwKICAgICJMb2NhdGlvbiBERVNDIgopLCAiV0hFUkUiOiB7CiAgICBDbGFzcyA9ICIlM2YiOwogICAgTG9jYXRpb24gPSAzYjsKfV0=
//but it should be eyJXSEVSRSI6eyJDbGFzcyI6IiUzZiIsIkxvY2F0aW9uIjoiM2IifSwiT1JERVIiOlsiRGF5IEFTQyIsIkxvY2F0aW9uIERFU0MiXX0=
I would be glad if someone could explain to me what I'm doing wrong and how I can reproduce the given Base64-string.
Since I'm new to this website I apologize in advance for wrong layout or other possible conventions I don't know.

Could you try this please? Is this what you wanted to do? It should convert a Dictionary to base64 String
func jsonToBaseString (yourJSON: [String: String]) -> String? {
do {
let jsonData = try JSONSerialization.data(withJSONObject: yourJSON, options: JSONSerialization.WritingOptions.prettyPrinted)
return
jsonData.base64EncodedString(options: .endLineWithCarriageReturn)
} catch {
return nil
}
}
Dictionary is Swifts JSON representation...

Two important things to understand:
What you are calling an array is not an array, it's a JSON dictionary (containing an array for the ORDER key).
Be careful not to confuse the syntax of arrays and dictionaries between Swift and JSON.
In Swift, an array: [0, 1], a dictionary: ["a":0, "b":1].
In JSON, an array: [0, 1], a dictionary: {"a":0, "b":1}.
A Swift dictionary is an unordered collection. There's no "position" for key-value pairs.

You'll need to change several things:
Your input string (not serialized) is not an array, but a JSON object.
Try constructing your string with a proper JSON library, such as SwiftyJSON.
String(array) is not enough to consistently convert your objects to strings. You should use a JSON serializer (such as SwiftyJSON json.rawString()).

let follow recommendation to use some json serialization, but take in account that
{
"alfa": 1,
"beta": true
}
and
{"beta":true,"alfa":1}
represents in JSON notation the same object even though their string representation ( doesn't matter if base64 encoded or not ) are different.

Related

How to store array of dictionaries into class variable?

I have an array of dictionaries that is being read in from a JSON file as seen below. I would like to store that value (jsonResult) into a class variable so that I can use it to populate a tableview. However, I don't quite understand how to store that value.
Here is how I am getting my array of dictionaries (jsonResult):
if let path = Bundle.main.path(forResource: filename, ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves) as! [String:Any]
self.tableData = jsonResult // WHAT GOES HERE?
} catch {
// handle error
}
}
And this is my class variable that I want to store my array of dictionaries into:
var tableData = [Dictionary<String, String>]()
How can I correctly store jsonResult into tableData? I do not want to use a struct as the structure of the dictionaries can vary.
You state the JSON is an array of dictionary but you are casting the result of JSONSerialization.jsonObject to just a dictionary. Since you seem to be expected an array of dictionary with both string keys and values, cast the result accordingly. But do it safely. Never use ! when working with JSON.
if let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves) as? [[String:String]] {
self.tableData = jsonResult
} else {
// Error - unexpected JSON result
}
This assumes you want the top level of the JSON result. If in fact jsonResult should be a dictionary and that top-level dictionary has a key to the actual array of dictionary you want then you need to fix the code accordingly.

Swift 3 - How to read a json output from an index that contains a string

I'm using an api to get some json information, here is an example what it looks like:
multimedia: [
{
url: "small.jpg"
format: "small"
},
{
url: "medium.jpg"
format: "medium"
},
{ url: "large.jpg"
format: "large"
}
]
so as you can see the multimedia container has many image urls with different sizes, and I want to read only one of them for example only the url for the medium.jpg.
My code is something like this that reads the multimedia container first:
let randomImg = result[random]["multimedia"] as! [[String: AnyObject]]
Then I made another variable which reads the index I want:
let randomImgIndex = randomImg[1]
so when I try to get my final string I get error:
let finalImgUrl = randomImgIndex[random]["url"] as! String
Error: Cannot subscript a value of type '[String : AnyObject]' with an index of type 'Int'
I am very new to swift so I'm totally lost.
randomImgIndex is already the desired dictionary.
Change:
let finalImgUrl = randomImgIndex[random]["url"] as! String
to:
let finalImgUrl = randomImgIndex["url"] as! String
On a side note, you need to stop using all of those ! and as! operators. Your app will crash if the data isn't exactly as expected. Code defensively when you are obtaining data beyond your control.

Is there approach to map dictionary keys/values simultaneously in Swift?

Referring to: Swift Standard Library > Dictionary > map(_:)
Returns an array containing the results of mapping the given closure
over the sequence’s elements.
As mentioned, we can do mapping in dictionaries, but the output will be an array, not a "mapped" dictionary.
Honestly, I'm not pretty sure if saying "mapping the whole dictionary" is legal, but what I mean is the following:
Consider that we have:
let myDict = ["1": "one","2": "tow","3": "three"]
and we want to map the whole thing! (both keys and values). Output should be:
let mappedDict = ["03": "THREE", "02": "TOW", "01": "ONE"]
Let's assume that the goal of the mapping is to add "0" as a first character to all keys and let all values to be upper-cased.
To make it more readable, I posted a solution (what I tried) as an answer instead of mentioning it in the question it self; I think my answer is not so elegant (or at least how I feel about its code smell), I mapped the keys, the values and combine them in a dictionary, each step has been achieved independently.
So, What I am asking about is:
Is there a way to do this job directly in one step? Something similar to:
This snippet is a demonstration of what I'm asking about, code won't work fine
let myDict = ["1": "one","2": "tow","3": "three"]
let mappedDict = myDict.map { key, value in
"0" + key
value.uppercased()
}
Thanks in advance.
How about this?
let myDict = ["1": "one","2": "tow","3": "three"]
let mappedDict = myDict.reduce([:]) { (result, pair) -> [String: String] in
var result = result
result["0" + pair.key] = pair.value.uppercased()
return result
}
You can achieve this by doing the following:
let myDict = ["1": "one","2": "tow","3": "three"]
let mappedKeys = myDict.map { "0" + $0.key } // ["02", "01", "03"]
let mappedValues = myDict.map { $0.value.uppercased() } // ["TOW", "ONE", "THREE"]
var mappedDict = [String: String]()
let zippedArray = Array((zip(mappedKeys, mappedValues)))
for element in zippedArray {
mappedDict[element.0] = element.1
}
print(mappedDict) // ["03": "THREE", "02": "TOW", "01": "ONE"]
To be more clear, the above code snippet doing the following:
Mapping the dictionary keys.
Mapping the dictionary values.
Create a new empty dictionary mappedDict to append to it.
Combining mapped keys/values into zippedArray (using zip).
Filling mappedDict via for-loop.

PFObject Array Sort

I'm using Parse and I have an array of PFObjects called "scorecardData". Each PFObject has a "score" property that is of type Int. I'm trying to sort my array by "score" but I'm getting the following error: "Binary operator '<' cannot be applied to two 'AnyObject?' operands". I'm not sure what I'm doing wrong here. I also tried down casting the objectForKey("score") as! Int but its not letting me do this. Any suggestions? Thanks in advance.
var scorecardData = [PFObject]()
scorecardData.sortInPlace({$0.objectForKey("score") < $1.objectForKey("score")})
You declared scorecardData variable as Array of PFObject. Why are you trying access PFObject property using objectForKey: reserved? Anyway I am not parse expert. But if you declared your array as [PFObject] you can use:
scorecardData.sortInPlace({$0.score < $1.score})
But this won't work unless you subclass PFObject for a more native object-oriented class structure. If you do that remember also to specify:
var scorecardData = [YOUR_NEW_CLASS]()
I strongly recommend subclassing PFObject to make use of all swift type-safe goodies.
But if you want to keep your data structure you can use:
scorecardData.sortInPlace({($0["score"] as! Int) < ($1["score"] as! Int)})
Keep in mind that it's dangerous, and in future avoid it.
If you want to Sort your array of PFOject... You can do this
extension Array where Element:PFObject {
func sort() -> [PFObject] {
return sort { (first, second) -> Bool in
let firstDate = first.objectForKey("time") as! NSDate//objectForKey(Constants.Parse.Fields.User.fullName) as? String
let secondDate = second.objectForKey("time") as! NSDate//objectForKey(Constants.Parse.Fields.User.fullName) as? String
return firstDate.compare(secondDate) == .OrderedAscending
}
}
}
Have you tried doing this?
var query = PFQuery(className:"ScoreCard")
// Sorts the results in ascending order by the score field
query.orderByDescending("score")
query.findObjectsInBackgroundWithBlock {

Convert [String]? to String in Swift

For my project, I extracted tweets from a CSV file in Swift. Problem is now all tweets are parsed as one element in an array, separated by ",".
let tweetsOfColumns = columns["tweet"]
let seperatedColumns = tweetsOfColumns.componentsSeparatedByString(",")
Error message: '[String]?' does not have a member named
'componentsSeparatedByString'.
I checked if tweetsOfColumns contains multiple elements, but it doesn't allow me to subscript with tweetsOfColumns[index].
Looking at the link you reference, columns["tweets"] is going to give you back an array of the values from the "tweets" column, so it's what you need already, there's no additional comma's to split things on, you just need:
let seperatedColumns = columns["tweet"]
to have an array containing the tweet column for each row.
When you try to get an element from a dictionary, like
columns["tweet"]
it will give you back an optional, because if there is nothing associated with the key, it gives you back nil (None), otherwise the value wrapped in an optional (Some(data)).
So you have to unwrap the optional for example:
columns["tweet"]!
You have to either use the optional ? to access the string:
let seperatedColumns = tweetsOfColumns?.componentsSeparatedByString(",")
But you should unwrap it:
if let unwrappedTweets = tweetsOfColumns?.componentsSeparatedByString(","){
let seperatedColumns = unwrappedTweets
}
The problem is probably that you'll get an optional back, which you have to unwrap. And the easiest and most elegant is to use the if-let unwrapper.
if let tweetsOfColumns = columns["tweet"] {
let seperatedColumns = tweetsOfColumns.componentsSeparatedByString(",")
// do something with the seperatedColumns
}
Based on David's question and the OP's response in the OP comments, you can use map on the Array returned by columns["tweet"]. Please post actual data/code in the future.
let columns = [
"tweet":["handleX,tag1,tag2,textA,textB",
"handleY,tag1,tag2,textC,textD"]]
var chunk = [[String]]()
if columns["tweet"] != nil {
chunk = columns["tweet"]!.map {
return $0.componentsSeparatedByString(",")
}
}

Resources