netreconlab / Parse-Swift

The original (OG) Swift SDK for Parse Platform (iOS, macOS, watchOS, tvOS, visionOS, Linux, Android, Windows). This repo is maintained by Parse-Swift's original developer and all new features and bug fixes will occur here.
https://swiftpackageindex.com/netreconlab/Parse-Swift/documentation
Apache License 2.0
62 stars 7 forks source link

fields named "id" are skipped in nested objects #140

Closed wtholliday closed 3 months ago

wtholliday commented 10 months ago

New Issue Checklist

Issue Description

Fields named "id" are skipped in nested objects.

Steps to reproduce

    struct MyType: Hashable, Codable {
        // BUG: if we call this "id" then parse won't save it
        var id: String
    }

    struct MyParseObject: ParseObject {
        public var originalData: Data?
        public var objectId: String?
        public var createdAt: Date?
        public var updatedAt: Date?
        public var ACL: ParseSwift.ParseACL?

        var nested: MyType?
        public init() {}
    }

    func test_parseEncodeDictionary() throws {
        var myObject = MyParseObject()
        myObject.nested = MyType(id: "test1")

        let object = try ParseCoding.parseEncoder()
            .encode(myObject,
                    acl: nil,
                    collectChildren: true,
                    objectsSavedBeforeThisOne: nil,
                    filesSavedBeforeThisOne: nil)
        XCTExpectFailure("currently skipping id fields")
        XCTAssertEqual(String(decoding: object.encoded, as: UTF8.self), #"{"nested":{"id":"test1"}}"#)
    }

Actual Outcome

Fields named "id" are ignored and don't make it to the server.

Expected Outcome

Fields named "id" (not in ParseObjects) should still be saved or an error should be reported so the user knows to rename the field.

Environment

Client

Server

Database

cbaker6 commented 10 months ago

id is an internal keyword for all Parse classes and should not be used to name a property or else you will get unexpected behavior. This is not just for ParseSwift, but all of the SDKs. You can use a name like localId, key names you are not suppose to use are: https://github.com/netreconlab/Parse-Swift/blob/c0a3a452a20d9f3ab9cb2a2ea790d0359a3beca0/Sources/ParseSwift/Coding/ParseEncoder.swift#L75-L82

wtholliday commented 10 months ago

Why skip fields inside a struct which isn't a ParseObject? Seems like it could store them without any issues, since they are just represented as json strings on the server, right?

cbaker6 commented 10 months ago

If you think you have a better solution, feel free to open a PR with your suggestion.

cbaker6 commented 10 months ago

Reopening this, will see if I can provide a fix by the end of the week

roaring-b commented 10 months ago

Thanks, Corey! Discovering this the hard way was a huge pain.

cbaker6 commented 10 months ago

I looked further into this and attempting to provide a fix now will cause more harm than good. The ParseEncoder is tricky as there are a number of properties that shouldn't be sent to the server.

As a workaround, you can do any of the following:

  1. Not use property names that contain any of the keys being skipped by the SDK, https://github.com/netreconlab/Parse-Swift/issues/140#issuecomment-1872593121
  2. Encode your respective object that contains the keys being skipped to JSON string before storing it in your ParseObject (the string will be stored as-is and won't skip keys). You can then decode your JSON string whenever you fetch your object from the server. You could then add another property with a getter and setter that decodes the string in the getter and encodes the string on the setter similar to
cbaker6 commented 3 months ago

See #177 for fix