Scenario:
Example
, subclassed from PFObject
. Some sensitive properties are optional/can be nil.My approach:
getExample
for User B to callgetExample
queries those examples, deletes all sensitive properties on the results (or recreates the object with only allowed properties) and returns those filtered objects. Problem:
Example
and make it parse it automatically? (call to server is via PFCloud.callFunction(inBackground: ...)
)? Thank you (also, any architecture advise highly appreciated).
Decoding backend JSON responses in Swift is almost trivial nowadays, with theDecodable
protocol and the JSONDecoder
class from the Foundation library.
Here's an example with an optional field:
import Foundation
let jsonString1 = """
{
"stringField" : "stringValue",
"intField" : 1,
"floatField" : 1.1
}
"""
let jsonString2 = """
{
"stringField" : "stringValue",
"intField" : 1,
"floatField" : 1.1,
"optionalField" : "optionalValue"
}
"""
struct Response: Decodable {
let stringField: String
let intField: Int
let floatField: Double
let optionalField: String?
}
let response1 = try! JSONDecoder().decode(Response.self, from: jsonString1.data(using: .utf8)!)
// Response(stringField: "stringValue", intField: 1, floatField: 1.1, optionalField: nil)
let response2 = try! JSONDecoder().decode(Response.self, from: jsonString2.data(using: .utf8)!)
// Response(stringField: "stringValue", intField: 1, floatField: 1.1, optionalField: Optional("optionalValue"))
And one more example, with a nested object:
import Foundation
let jsonString = """
{
"stringField" : "stringValue",
"nestedObject" :
{
"intValue" : 2
}
}
"""
struct Response: Decodable {
struct NestedType: Decodable {
let intValue: Int
}
let stringField: String
let nestedObject: NestedType
}
let response = try! JSONDecoder().decode(Response.self, from: jsonString.data(using: .utf8)!)
// Response(stringField: "stringValue", nestedObject: __lldb_expr_10.Response.NestedType(intValue: 2))
An example with a date:
import Foundation
let jsonString = """
{
"date" : "2021-02-04 09:38:33.000"
}
"""
struct Response: Decodable {
let date: Date
}
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let response = try! decoder.decode(Response.self, from: jsonString.data(using: .utf8)!)
// Response(date: 2021-02-04 09:38:33 +0000)
Find out more here .
UPDATE
After discussion it became clear that the problem is that the object to parse is a dictionary containing objects that cannot be represented in JSON natively (eg Data
/ NSData
). In this case it's possible to convert them into something representable (eg for dates it can be a formatted String
). Here's an example involving NSDate
and JSONSerialization
:
import Foundation
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
let jsonDict1 = ["stringField" : "stringValue"]
let jsonData1 = try JSONSerialization.data(withJSONObject: jsonDict1)
let date = NSDate()
let jsonDict2: [String : Any] = ["stringField" : "stringValue",
"optionalDate" : dateFormatter.string(from: date as Date)]
let jsonData2 = try JSONSerialization.data(withJSONObject: jsonDict2)
struct Response: Decodable {
let stringField: String
let optionalDate: Date?
}
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let response1 = try! decoder.decode(Response.self, from: jsonData1)
// Response(stringField: "stringValue", optionalDate: nil)
let response2 = try! decoder.decode(Response.self, from: jsonData2)
// Response(stringField: "stringValue", optionalDate: Optional(2022-02-21 13:48:48 +0000))
After some back and forth I found a good solution that ensures automated parsing of redacted PFObject subclasses. The trick is to add the properties __type
and className
to the return value so that the objective-c Parse SDK can recognize the type automatically
Cloud Code (simplified):
Parse.Cloud.define("getExamples", async (request) => {
let query = new Parse.Query(ExampleClass);
query.limit(50);
let results = await query.find({useMasterKey:true});
let returnResults = [];
_.each(results, function(item) {
// make a JSON copy
let returnItem = item.toJSON();
// delete sensitive properties based on internal rules
if(thisSpecificUserShouldnotHaveAccess) {
delete returnItem["maybeSensitive"];
}
// Mark as valid PFObject (important!)
returnItem["__type"] = "Object";
returnItem["className"] = "Example";
returnResults.push(returnItem);
});
return returnResults;
});
Swift Class Code (simplified):
class Example: PFObject {
public static func parseClassName() -> String { "Example" }
@NSManaged public var title: String?
@NSManaged public var maybeSensitive: String? // will be nil based on server logic
}
Swift Retrieve Code (simplified):
func callFunction<T: PFObject>(name: String, parameters: [String: Any]? = nil) async throws -> [T] {
return try await withCheckedThrowingContinuation { continuation in
PFCloud.callFunction(inBackground: name, withParameters: parameters) { (result, error) in
do {
if let error = error {
throw error
}
if let parsed = result as? [T] {
continuation.resume(returning: parsed)
return
}
if let parsed = result as? T {
continuation.resume(returning: [parsed])
return
}
throw Error.notAPFObject // defined somewhere else
} catch {
continuation.resume(with: .failure(error))
}
}
}
}
Call to server:
let examples: [Example] = try await callFunction("getExamples")
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.