Swift 3 - invalid JSON object of [[String: Any]] - arrays

I have this instance:
var commands = [[String: Any]]()
After some calculations, its result is:
[["command": 50022, "homes": [["gateways": [["mac": "845DD74B405E", "services": [["iid": 9, "name": "Some name"]], "aid": 1]], "name": "H1"]]], ["command": 50025, "services": [["mac": "845DD74B405E", "iid": 9, "aid": 1, "new_name": "Some name"]]]]
I want to convert it (commands) to JSON using SwiftyJSON:
let json = JSON(commands)
print(json) // "unknown"
but the result is always unknown.
Then I check the validity of commands to make sure it is JSON-convertable. But I got false with JSONSerialization.isValidJSONObject(commands), which is unexpected!
Do you have any ideas why commands (array of Dictionary) is NOT a valid JSON object?
Thanks

The problem in my commands is at the integer numbers, 50022 and 50025. In my code, they are defined in an enum of UInt32. And when I copied them to a playground to verify, Swift converts it to Int. That is why I always succeed in playground.
Back to my code, Swift 3 expects a number to be an Int in order to convert it to a JSON object successfully. Swift 2 does not have this behavior.
So, after I change my enum define from UInt32 to Int, it just works beautifully!
Thanks everyone.

Related

Getting a __NSArray0 from a JWT

I have an api that creates a JWT for a logged in user and when I look at the decoded version of the token on jwt.io I get what I expect.
It shows that the data property is [] and it holds the users data when a user is logged in.
Now I am trying to decode this into swift for my iOS app, but the data in the data property comes back as:
(
)
and the type for this is __NSArray0
I can't loop through it or anything - what is a __NSArray0 and how would decode the token properly?
__NSArray0:
__NSArray means it's an NSArray object. It's the "old Objective-C immutable Array type". That's what gives you after decoding an JSON Array with (NS)JSONSerialization if you don't cast it into a Swift Array.
The 0 at the end is to say it's a specific NSArray, it's an NSArray empty, with no object. Why using that? Because in reality, it's a internal version of NSArray that is optimized for zero elements. So don't mind about it.
Since you have a NSArray, typical print of it, is:
(
)
So just cast it as an Array how its supposed type when non-empty to iterate. If you know it's an array of String, cast is as [String] and iterate over it.
It's the OpenStep format. Did you ever tried to read a PBXCodeProj?
Like more yourApp.xcodeproj/project.pbxproj It's there, you see how are printed NSDictionary, NSArray etc in that Format + comments.
In JSON, it's [] to show it's an Array, but in Objective-C the print is different.
Want to reproduce it?
let emptyNSArray = NSArray()
print("emptyNSArray:\n\(emptyNSArray)")
let castedEmptyAsArray = emptyNSArray as [AnyObject]
print("castedEmptyAsArray:\n\(castedEmptyAsArray)")
let nsArray = NSArray(array: ["Hello", "World", "!"])
print("nsArray:\n\(nsArray)")
let castedAsArray = nsArray as [AnyObject]
print("castedAsArray:\n\(castedAsArray)")
let emptyArrayJSON = Data("[]".utf8)
let decodedEmptyArrayDefault = try! JSONSerialization.jsonObject(with: emptyArrayJSON)
print("decodedEmptyArrayDefault:\n\(decodedEmptyArrayDefault)") //If you don't cast, it's by default a NSArray
let decodedEmptyArrayCasted = try! JSONSerialization.jsonObject(with: emptyArrayJSON) as! [AnyObject]
print("decodedEmptyArrayCasted:\n\(decodedEmptyArrayCasted)")
Output:
$>emptyNSArray:
(
)
$>castedEmptyAsArray:
[]
$>nsArray:
(
Hello,
World,
"!"
)
$>castedAsArray:
[Hello, World, !]
$>decodedEmptyArrayDefault:
(
)
$>decodedEmptyArrayCasted:
[]

Sorting Dictionary of type [String: [String: Any]] by Value

I'm working on an IOS project that gets its data from Google's Firebase Firestore.
I have Documents like this in Firestore:
5lTSobXhcQBR2oG95s5q
Title: "ABC"
Timestamp: 1554374528.641053
FEeIAlAPlcrVvvtSKn8D
Title: "XYZ"
Timestamp: 1554443702.1300058
In my IOS project I have a Dictionary like this:
myDictionary: [String: [String: Any]] = [5lTSobXhcQBR2oG95s5q: ["Title": "ABC", "Timestamp": 1554374528.641053], FEeIAlAPlcrVvvtSKn8D: ["Title": "XYZ", "Timestamp": 1554443702.1300058]]
How can I sort my Dictionary by Timestamp?
If you are OK with getting back an array of tuples you can apply sorted() with a closure
let sortedTuples = myDictionary.sorted() {
if let t1 = $0.value["Timestamp"] as? Double, let t2 = $1.value["Timestamp"] as? Double {
return t1 < t2
}
return true
}
Note that I by default return true here if either of the values can't be cast to double or doesn't exist. A more advanced logic can of course be implemented depending on which of the two elements fails.
Every collection in iOS or swift has a sort function.
For example:
let sortedArray = dictionary.sort() { return $0.timestamp < $1.timestamp }
$0 will reference to 'the first' element and $1 the element after the first one.
In your example, the logic is the same.
I would do it like this:
Extract the timestamp data and store it in an extra dictionary with the id as a key and the value as a timestamp. Then I would use the .sort function to sort these elements in my dictionary.

What is the meaning of []json.Rawmessage

What is the meaning of []json.Rawmessage. Its within this structure here:
type Request struct {
Jsonrpc string `json:"jsonrpc"`
Method string `json:"method"`
Params []json.RawMessage `json:"params"`
ID interface{} `json:"id"`
}
I know its a slice of type json. I do not understand what the .RawMessage is referring to. I tried looking it up in both golang tour and my golang book. Also ultimately I know Params is type []json.Rawmessage being bundled into another type called Request
Futhermore:
What is going on with these segments json:"jsonrpc". Is a string literal somehow being attached to var? Again this is not in golang tour or my golang book. Thanks for your time.
[] is defining a slice
json is the package import name (from your import statement above)
RawMessage is the type within the package. In this case for a []byte type.
json:"params" is a field tag. The json package reads it with reflection and determines what name to use for json.
Most of the time time you need to look in to the package doc rather than some book and the online tour.
The json:"jsonrpc" is a struct tag.
For this specific case the json.Marshal function will read that struct tag and return the JSON field name with the given value. For more about "encoding/json" struct tags usage: https://golang.org/pkg/encoding/json/#Marshal
For RawMessage, you can read more about it here https://golang.org/pkg/encoding/json/#RawMessage
type RawMessage []byte
Normally I use it when having a "generic" kind of JSON object that don't need to be process right the way (maybe I just send them to another service, or I will unmarshall it later depending on conditions). For example (I will use your Request struct):
jsonString := `[
{
"id": 123,
"method": "getSomething",
"params": [{"A": 1, "B": 2}]
}
{
"id": 123,
"method": "getSomethingElse",
"params": [{"C": 1, "D": 2}]
}
]`
With this processing code:
var requests []Request
json.Unmarshal([]byte(jsonString), &requests)
// if no error, you will have 2 items in requests
// requests[0].Params[0] is equal to []byte(`{"A": 1, "B": 2}`)
// requests[1].Params[0] is equal to []byte(`{"C": 1, "D": 2}`)
for _, req := range requests {
if req.Method == "getSomething" {
justProxyThisRequestToAnotherService(req)
} else if req.Method == "getSomethingElse" {
var params []map[string]int
json.Unmarshal(req.Params, &params)
// then do something with params
}
}

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

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.

How to assign a Dictionary to AnyObject in swift

I have been playing around with Swift. I have had multiple errors with types, especially working with Swift and my old Objective-C classes. The problem with this method is: I am expecting an array made of NSDictionarys in Objective-C.
var curArr:[Dictionary<String, AnyObject>] = self.getItemsForCurrentStack()
var arrToReturn:[(Dictionary<String, AnyObject?>)] = []
for obj in curArr{
arrToReturn.append(["image": UIImage(named: obj["imageName"] as! String), "color": UIColor(red:obj["colorDic"]!["red"] as! CGFloat, green:obj["colorDic"]!["green"] as! CGFloat, blue:obj["colorDic"]!["blue"] as! CGFloat, alpha:1.0), "percentage": obj["percantage"]])
}
return arrToReturn
This returns Dictionaries (which are NSDictionaries) in Swift. But the last line throws me an error:
Dictionary' is not identical to 'AnyObject'
I've tried using as! [AnyObject]
But that throws another error:
'AnyObject' is not a subtype of 'Dictionary'
I don't get the second error, since this doesn't have to be a subtype, but the other way around. Any ideas on how to solve this? I didn't find an answer for several hours of googleing and researching.
Dictionary is a struct in Swift, whereas AnyObject is
/// The protocol to which all classes implicitly conform.
Depending what you're trying to do, you may want to use Any in your code, or cast your dictionary to NSDictionary using as NSDictionary.
Edit after your clarification:
If you split up the append call from the dictionary itself, you see a better error message:
So, the issue is that your dictionary contains some Optional values, but Optional is a struct and not convertible to Obj-C. You can fix by casting to UIImage! and AnyObject! (ImplicitlyUnwrappedOptional), or by using as!.
Your code works fine in playground, without compile or runtime errors. Here is what I added to populate curArr:
var curArr:[Dictionary<String, AnyObject>] = [
[
"imageName" : "photo.jpg",
"colorDic" : ["red" : 1.0, "green" : 0.0, "blue" : 0.0],
"percentage" : 0.5
]
]
With all this forced unwrapping I think you just have to make sure that what self.getItemsForCurrentStack() returns is indeed what you expect.
Result in playground:
[
"percentage": {Some 5.0e+-1},
"image": nil,
"color": {Some r 1,0 g 0,0 b 0,0 a 1,0}
]
My recommendation would be to refactor into objects - that would make your code so much more readable!
In Swift 3
code:
var curArr:[Dictionary<String, Any>] = [
[
"imageName" : "photo.jpg",
"colorDic" :[
"red" : 1.0,
"green" : 0.0,
"blue" : 0.0
],
"percentage" : 0.5
]
]

Resources