parse-community / ParseLiveQuery-iOS-OSX

Parse LiveQuery Client for iOS/OS X.
https://parseplatform.org
Other
192 stars 133 forks source link

Emitted Events from WebSocket pings not forwarded to Subscription Handler #264

Open HackShitUp opened 1 year ago

HackShitUp commented 1 year ago

New Issue Checklist

Issue Description

subscription?.handleEvent({ (query: PFQuery<PFObject>, event: Event<PFObject>) in
    print("Fired: \(#line)")
})

The Subscription.handleEvent protocol never gets called but WebSocket pings are received (logged in Client.shouldPrintWebSocketTrace)

Steps to reproduce

Define the client, and subscribe to a query the standard way:

let query = PFQuery(className: "Test")
query.whereKey("objectId", equalTo: "objectId")

client = ParseLiveQuery.Client(server: serverURL)
client?.shouldPrintWebSocketLog = true
client?.shouldPrintWebSocketTrace = true

subscription = client?.subscribe(query)
subscription?.handleEvent({ (query: PFQuery<PFObject>, event: Event<PFObject>) in
    print("Fired: \(#line)")
})

WebSocket trace logs received ping but does not forward to the protocol.

Actual Outcome

Event not forwarded at the ParseLiveQuery layer

Expected Outcome

All emitted events should be accessible by the Subscription's handler

Environment

Parse LiveQuery ObjC SDK

Server

parse-github-assistant[bot] commented 1 year ago

Thanks for opening this issue!

432player commented 1 year ago

I had the same issue. Server Side: "parse": "4.0.1", "parse-server": "^6.0.0"

IOS Client Side:

Apparently from some version of parse-server the received message from the ParseLiveQuery didn't return with the type on the main object in the string received. for example: { "op": "update", "clientId": "559ca4bd-7425-4ea8-b7ed-8ac73e6c6d15", "requestId": 2, "object": { "user1": { "type": "Pointer", "className": "_User", "objectId": "8YoD1R8RTJ" }, "user2": { "__type": "Pointer", "className": "_User", "objectId": "DrvZzHZASt" }, "createdAt": "2023-05-16T09:57:29.412Z", "updatedAt": "2023-05-20T20:13:46.736Z", "messages": { "type": "Relation", "className": "Message" }, "className": "PrivateChat", "objectId": "lsxP3br9EG" }, "original": { "user1": { "type": "Pointer", "className": "_User", "objectId": "8YoD1R8RTJ" }, "user2": { "__type": "Pointer", "className": "_User", "objectId": "DrvZzHZASt" }, "createdAt": "2023-05-16T09:57:29.412Z", "updatedAt": "2023-05-20T20:12:07.365Z", "messages": { "type": "Relation", "className": "Message" }, "className": "PrivateChat", "objectId": "lsxP3br9EG" } } As you can see "object" has no type so parse doesn't know how to parse it into a PFObject. that's why it says something like "bad json" when serializing it into PFObject.

So I've added a short code to always add a __type = "Object" into the Json for the first main "object" and I removed the original object, I don't see how original object is supported in the ParseLiveQuery-iOS-OSX library.

So the received json would now look like this: { "op": "update", "clientId": "559ca4bd-7425-4ea8-b7ed-8ac73e6c6d15", "requestId": 2, "object": { "user1": { "type": "Pointer", "className": "_User", "objectId": "8YoD1R8RTJ" }, "user2": { "__type": "Pointer", "className": "_User", "objectId": "DrvZzHZASt" }, "createdAt": "2023-05-16T09:57:29.412Z", "updatedAt": "2023-05-20T20:13:46.736Z", "messages": { "type": "Relation", "className": "Message" }, "__type": "Object", //This is what I've added, and removed "original" key from the json. "className": "PrivateChat", "objectId": "lsxP3br9EG" } }

So in the function func handleOperationAsync( string: String) -> Task in ClientPrivate.swift I've changed the code to look like this: func handleOperationAsync( string: String) -> Task { return Task(.queue(queue)) { if self.shouldPrintWebSocketTrace { NSLog("ParseLiveQuery: Received message: (string)") } guard let jsonData = string.data(using: .utf8), let jsonDecoded = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: AnyObject] else { throw LiveQueryErrors.InvalidResponseError(response: string) }

//--------START CHANGED CODE HERE------// var modifiedJson = jsonDecoded // Remove the "original" key if present modifiedJson.removeValue(forKey: "original")

        if let object = modifiedJson["object"] as? [String: AnyObject] {
            modifiedJson["object"] = addTypeToObject(object) as AnyObject
        }

        guard let response: ServerResponse = try? ServerResponse(json: modifiedJson) else {
            throw LiveQueryErrors.InvalidResponseError(response: string)
        }

        func addTypeToObject(_ object: [String: AnyObject]) -> [String: AnyObject] {
            var modifiedObject = object
            for (key, value) in modifiedObject {
                if key == "className" && !(value is [String: AnyObject]) {
                    modifiedObject["__type"] = "Object" as AnyObject
                    break
                }
            }
            return modifiedObject
        }

//--------END CHANGED CODE HERE------// switch response { case .connected: let sessionToken = PFUser.current()?.sessionToken self.subscriptions.forEach { _ = self.sendOperationAsync(.subscribe(requestId: $0.requestId, query: $0.query, sessionToken: sessionToken)) }

        case .redirect:
            // TODO: Handle redirect.
            break

        case .subscribed(let requestId):
            self.subscriptionRecord(requestId)?.subscribeHandlerClosure(self)

        case .unsubscribed(let requestId):
            guard
                let recordIndex = self.subscriptions.firstIndex(where: { $0.requestId == requestId })
                 else {
                    break
            }
            let record: SubscriptionRecord = self.subscriptions[recordIndex]
            record.unsubscribeHandlerClosure(self)
            self.subscriptions.remove(at: recordIndex)

        case .create, .delete, .enter, .leave, .update:
            var requestId: RequestId = RequestId(value: 0)
            guard
                let event: Event<PFObject> = try? Event(serverResponse: response, requestId: &requestId),
                let record = self.subscriptionRecord(requestId)
            else {
                break
            }

            record.eventHandlerClosure(event, self)
        case .error(let requestId, let code, let error, let reconnect):
            let error = LiveQueryErrors.ServerReportedError(code: code, error: error, reconnect: reconnect)
            if let requestId = requestId {
                self.subscriptionRecord(requestId)?.errorHandlerClosure(error, self)
            } else {
                throw error
            }
        }
    }
}

I hope that this will be taken care of by the library itself cause if I update the library I will always have to remember to add this code. Perhaps if there's is no type assume that it is "__type" = "Object" or something if it's missing, not really sure what the best way would be. but I would appreciate it if this can be fixed internally.

Much blessings

mtrezza commented 1 year ago

Does anyone want to open a PR and propose a solution?

mtrezza commented 1 year ago

@432player, @HackShitUp The LiveQuery feature has been added as a module to the Parse Apple SDK; could you please verify that this issue is fixed there, and if not open a new issue there?