bignerdranch / Freddy

A reusable framework for parsing JSON in Swift.
MIT License
1.09k stars 120 forks source link

Payload notification #250

Closed YMonnier closed 7 years ago

YMonnier commented 7 years ago

Hello,

I have noticed that the parsing of payload notification is not good. Maybe it is my parsing way is not good...

When I send this data push notification:

"people": {
            "123":{"name":"Jane Doe", "age":35},
            "124":{"name":"John Doe", "age":37}
}

I cannot get people as dictionary:

/// Analysis the payload notification from didReceiveRemoteNotification function.
func treat(notification value: [AnyHashable: Any]) {
    log.info("Notification: \n\(value)\n")
    let json = JSONSerialization.makeJSON(with: value)
    let people = try json.getDictionary(at: "people")
    log.debug(people)
} 
Log: 
2017-04-08 13:26:37.894 [Info] [AppDelegate+PushNotification.swift:166] treat(notification:) > Notification: 
[AnyHashable("gcm.message_id"): 0:1491650797197279%6435762e6435762e, AnyHashable("aps"): {
    "content-available" = 1;
}, AnyHashable("people"): {"123":{"name":"Jane Doe","age":35},"124":{"name":"John Doe","age":37}}]

The operation couldn’t be completed. (Freddy.JSON.Error error 3.)

Moreover, I can simply get the people data like this:

if let test = value["people"] as? [String: Any] {
    log.debug(test)
}

Did I forget something using Freddy?

mdmathias commented 7 years ago

I'm not sure what the issue is. The below works fine for me.

func testAnyHashableAny() throws {
    let response: [AnyHashable: Any] = ["people": ["123": ["name": "Jane Doe", "age": 35],
                                                   "124": ["name": "John Doe", "age": 37]]]

    let json = JSONSerialization.makeJSON(with: response)
    let people = try json.getDictionary(at: "people")
    print(String(describing: people["123"])) // Optional(["name": Jane Doe, "age": 35])
    print(String(describing: people["124"])) // Optional(["name": John Doe, "age": 37])
}

Could you share something to help us reproduce the error?

YMonnier commented 7 years ago

I am using Firebase to receive push notification.

Here my scenario:

Sending message data to a specific device:

POST  https://fcm.googleapis.com/fcm/send
{
    "to": "...iOS_Token_Device...",
    "content_available": true,
    "data": {
        "people": {
                "123": {"name":"Jane Doe", "age":35},
                "124": {"name":"John Doe", "age":37}
        }
    }
}

I receive this message from applicationReceivedRemoteMessage delegate function:

[AnyHashable("gcm.message_id"): 0:14916635823233, AnyHashable("aps"): {
    "content-available" = 1;
}, AnyHashable("people"): {"123":{"name":"Jane Doe","age":35},"124":{"name":"John Doe","age":37}}]

Then I try to convert this dictionary to JSON:

func treat(notification value: [AnyHashable: Any]) throws {
        let json = JSONSerialization.makeJSON(with: value)
        log.debug("JSON: \n\(json)\n")
        let messageId = try json.getString(at: "gcm.message_id")
        log.debug("messageId: \(messageId)")
        let people = try json.getDictionary(at: "people") // Exception!
        log.debug("people: \(people)")
}
Log: 
2017-04-08 16:59:48.424 [Debug] [AppDelegate+PushNotification.swift:187] treat(notification:) > JSON: 
["gcm.message_id": 0:1491663582393746%6ddd, "aps": ["content-available": 1], "people": {"123":{"name":"Jane Doe","age":35},"124":{"name":"John Doe","age":37}}]
2017-04-08 17:16:16.850 [Debug] [AppDelegate+PushNotification.swift:176] treat(notification:) > messageId: 0:1491663582393746%6ddd

When I am trying to get people dictionary an exception is thrown:

expression unexpectedly raised an error: Freddy.JSON.Error.valueNotConvertible(value: {"123":{"name":"Jane Doe","age":35},"124":{"name":"John Doe","age":37}}, to: Swift.Dictionary<Swift.String, Freddy.JSON>)
mdmathias commented 7 years ago

getDictionary(at:) wants to return a [String: JSON]. The error is telling you that it cannot convert whatever is at the key "people" to be that type.

Can you inspect json and let me know here what is in it (not the log printed to the console, but the types) after you makeJSON(with: value)? I'm curious if you can see what the type for the value is for the "people" key.

YMonnier commented 7 years ago

Yes I can see the type.

screen shot 2017-04-08 at 22 26 38

The type for the "people" key is a string...

So I tried todo something like that:

let json = JSONSerialization.makeJSON(with: value)
let peopleString = try json.getString(at: "people")

let peopleJSON = try JSON(jsonString: peopleString)
let people123 = try peopleJSON.getDictionary(at: "123")

And it works

2017-04-08 22:51:18.830 [Debug] [AppDelegate+PushNotification.swift:172] treat(notification:) > JSON: 
["gcm.message_id": 0:1491684678179017%6435762e6435762e, "aps": ["content-available": 1], "people": {"123":{"name":"Jane Doe","age":35},"124":{"name":"John Doe","age":37}}]

2017-04-08 22:51:18.832 [Debug] [AppDelegate+PushNotification.swift:175] treat(notification:) > people String: {"123":{"name":"Jane Doe","age":35},"124":{"name":"John Doe","age":37}}
2017-04-08 22:51:18.846 [Debug] [AppDelegate+PushNotification.swift:177] treat(notification:) > People JSON: ["124": ["name": John Doe, "age": 37], "123": ["name": Jane Doe, "age": 35]]
2017-04-08 22:51:18.846 [Debug] [AppDelegate+PushNotification.swift:180] treat(notification:) > People 123 JSON: ["name": Jane Doe, "age": 35]

I would like to know why the makeJSON function does not convert the people dictionary?

As I see how the makeJSON function is written, the parsing should be work properly!

Regards,

mdmathias commented 7 years ago

makeJSON will just match on what it is given. My guess is that the data in question is sent down as string; so, that's what Freddy will make of it.

I don't know if you have any control over the server, but if you do, it's worth taking a look at what's going on there.