Hi guys.
When I subscribe object by LiveQuery, data can not be decoded to PHObject, code:
import Foundation
import ParseCore
import ParseLiveQuery
class Suggestion: PFObject, PFSubclassing {
@NSManaged var title: String?
@NSManaged var document: Document?
class func parseClassName() -> String {
return "Suggestion"
}
}
class Suggestions {
let subscribe: Subscription<Suggestion>
init() {
ParseLiveQuery.Client.shared.shouldPrintWebSocketTrace = true
subscribe = ParseLiveQuery.Client.shared.subscribe(
Suggestion.query()!.includeAll() as! PFQuery<Suggestion>
)
.handleEvent {
log.I("recive \($1)") // never logging
}
}
}
Reiceived Json on WebSocket callback:
{
"op":"delete",
"clientId":"d81000da-4f64-4819-a470-8aade320322a",
"requestId":2,
"object":{
// I think should have a "__type": "Object" here
"document":{
"__type":"Pointer",
"className":"Document",
"objectId":"EtZCPMTygF"
},
"title":"Suggestion 20",
"createdAt":"2024-01-18T13:41:59.930Z",
"updatedAt":"2024-01-18T13:41:59.930Z",
"className":"Suggestion",
"objectId":"aN94A8fXYB"
}
}
I checked the code in PHDecoder and found that because of no "__type": "Object" in "object", it can not be decoded to PHObject or its subclass.
PHDecoder snippet(line 43):
- (id)decodeDictionary:(NSDictionary *)dictionary {
NSString *op = dictionary[@"__op"];
if (op) {
return [[PFFieldOperationDecoder defaultDecoder] decode:dictionary withDecoder:self];
}
NSString *type = dictionary[@"__type"];
if (type) {
if ([type isEqualToString:@"Date"]) {
return [[PFDateFormatter sharedFormatter] dateFromString:dictionary[@"iso"]];
} else if ([type isEqualToString:@"Bytes"]) {
return [PFBase64Encoder dataFromBase64String:dictionary[@"base64"]];
} else if ([type isEqualToString:@"GeoPoint"]) {
return [PFGeoPoint geoPointWithDictionary:dictionary];
} else if ([type isEqualToString:@"Polygon"]) {
return [PFPolygon polygonWithDictionary:dictionary];
} else if ([type isEqualToString:@"Relation"]) {
return [PFRelation relationFromDictionary:dictionary withDecoder:self];
} else if ([type isEqualToString:@"File"]) {
return [PFFileObject fileObjectWithName:dictionary[@"name"]
url:dictionary[@"url"]];
} else if ([type isEqualToString:@"Pointer"]) {
NSString *objectId = dictionary[@"objectId"];
NSString *localId = dictionary[@"localId"];
NSString *className = dictionary[@"className"];
if (localId) {
// This is a PFObject deserialized off the local disk, which has a localId
// that will need to be resolved before the object can be sent over the network.
// Its localId should be known to PFObjectLocalIdStore.
return [self _decodePointerForClassName:className localId:localId];
} else {
return [self _decodePointerForClassName:className objectId:objectId];
}
} else if ([type isEqualToString:@"Object"]) {
// π¨π¨π¨π¨π¨π¨π¨π¨
// Should go here, but no "__type": "Object" in "object", so it can not be decoded to PHObject or its subclass.
// π¨π¨π¨π¨π¨π¨π¨π¨
NSString *className = dictionary[@"className"];
NSMutableDictionary *data = [dictionary mutableCopy];
[data removeObjectForKey:@"__type"];
[data removeObjectForKey:@"className"];
NSDictionary *result = [self decodeDictionary:data];
return [PFObject _objectFromDictionary:result
defaultClassName:className
completeData:YES
decoder:self];
} else {
// We don't know how to decode this, so just leave it as a dictionary.
return dictionary;
}
}
NSMutableDictionary *newDictionary = [NSMutableDictionary dictionaryWithCapacity:dictionary.count];
[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
newDictionary[key] = [self decodeObject:obj];
}];
return newDictionary;
}
And I added some workaround code to ParseLiveQuery/ParseLiveQuery/Internal/ClientPrivate.swift, everything is working fine. here is the code:
private func parseObject<T: PFObject>(_ objectDictionary: [String:AnyObject]) throws -> T {
guard let _ = objectDictionary["className"] as? String else {
throw LiveQueryErrors.InvalidJSONError(json: objectDictionary, expectedKey: "parseClassName")
}
guard let _ = objectDictionary["objectId"] as? String else {
throw LiveQueryErrors.InvalidJSONError(json: objectDictionary, expectedKey: "objectId")
}
// π¨π¨π¨new code π
var dict = objectDictionary
dict["__type"] = "Object" as AnyObject
// π¨π¨π¨new code π
// π¨π¨π¨ guard let object = PFDecoder.object().decode(objectDictionary) as? T else { change to π
guard let object = PFDecoder.object().decode(dict) as? T else {
throw LiveQueryErrors.InvalidJSONObject(json: objectDictionary, details: "cannot decode json into \(T.self)")
}
return object
}
So, is this a bug on the Server Side or the Client Side? Any better solution?
Thanks a lot.
Steps to reproduce
Create a class named "Suggestion" by Parse Dashboard with a column named "title"
Subscribe "Suggestion" class by LiveQuery on iOS Client Side
Create, Update, or Delete a "Suggestion" object on Parse Dashboard
Environment
Client
Parse ObjC SDK version: 2.7.3
Server
Parse Server version: 6.4.0
Operating system: macOS 14.1.2 (23B92)
Local or remote host (AWS, Azure, Google Cloud, Heroku, Digital Ocean, etc): Local Dev Environment
Database
System (MongoDB or Postgres): MongoDB
Database version: 6.0.2
Local or remote host (MongoDB Atlas, mLab, AWS, Azure, Google Cloud, etc): Local
Issue Description
Hi guys. When I subscribe object by
LiveQuery
, data can not be decoded toPHObject
, code:Reiceived Json on WebSocket callback:
I checked the code in
PHDecoder
and found that because of no"__type": "Object"
in"object"
, it can not be decoded toPHObject
or its subclass.PHDecoder
snippet(line 43):And I added some
workaround
code toParseLiveQuery/ParseLiveQuery/Internal/ClientPrivate.swift
, everything is working fine. here is the code:So, is this a bug on the Server Side or the Client Side? Any better solution? Thanks a lot.
Steps to reproduce
Environment
Client
2.7.3
Server
6.4.0
macOS 14.1.2 (23B92)
Local Dev Environment
Database
MongoDB
6.0.2
Local