Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting property to nil doesn't save to database #264

Closed
4 tasks done
Vortec4800 opened this issue Oct 21, 2021 · 9 comments
Closed
4 tasks done

Setting property to nil doesn't save to database #264

Vortec4800 opened this issue Oct 21, 2021 · 9 comments
Labels
type:feature New feature or improvement of existing feature

Comments

@Vortec4800
Copy link

New Issue Checklist

Issue Description

Setting a value on an optional model property doesn't propagate to the server.

Steps to reproduce

struct TestObject: ParseObject {
	// ParseObject Conformance
	var objectId: String?
	var createdAt: Date?
	var updatedAt: Date?
	var ACL: ParseACL?

	var testValue: String?
}

var testObj = TestObject()
testObj.testValue = "value"

testObj = try! testObj.save()
// Break here and check value on dashboard

testObj.testValue = nil

testObj = try! testObj.save()
// Break here and check value again on dashboard, see that it still says "value" and not (undefined)

Expected Outcome

Final value on dashboard should be (undefined) or nil, instead of keeping the old value.

Environment

Client

  • Parse Swift SDK version: 2.0.2
  • Xcode version: 13.0
  • Operating system (iOS, macOS, watchOS, etc.): macOS
  • Operating system version: 11.6

Server

  • Parse Server version: 4.10.4
  • Operating system: Ubuntu
  • Local or remote host (AWS, Azure, Google Cloud, Heroku, Digital Ocean, etc): Digital Ocean

Database

  • System (MongoDB or Postgres): MongoDB
  • Database version: 4.4
  • Local or remote host (MongoDB Atlas, mLab, AWS, Azure, Google Cloud, etc): Digital Ocean
@parse-github-assistant
Copy link

Thanks for opening this issue!

  • ❌ Please edit your post and use the provided template when creating a new issue. This helps everyone to understand your post better and asks for essential information to quicker review the issue.

@cbaker6 cbaker6 added the type:question Support or code-level question label Oct 22, 2021
@cbaker6
Copy link
Contributor

cbaker6 commented Oct 22, 2021

The Swift SDK uses a tailored JSONEncoder. The Apple JSONEncoder ignores nil, so the behavior you mentioned is expected. You can see workarounds and comments about this:

The easiest way to achieve what you are looking for is by using ParseOperation, particularly unset. More in the SDK documentation. Every ParseObject has an operation available. This can be done two ways:

var testObj = TestObject()
let operations = testObj.operation.unset("testValue") // Unset on the server
testObj = try await operations.save()

// or unset on the server and on the local ParseObject
let operations = testObj.operation.unset(("testValue", \.testValue))
testObj = try await operations.save()

// or unset multiple during the same save on the server and locally
let operations = testObj.operation
  .unset(("testValue", \.testValue))
  .unset(("testValue2", \.testValue2))
testObj = try await operations.save()

If you need to access the custom ParseEncoder, see the post here: https://community.parseplatform.org/t/parsefile-init-without-previous-fetch/1666/6?u=cbaker6

You can also access the encoders using a static ParseObject, TestObject.getEncoder()

@Vortec4800
Copy link
Author

I see. Given the movement in that Swift thread, it doesn't seem like this is going to be addressed by the Swift Encoder any time soon.

It does seem like this would be expected behavior for the SDK, given the behavior of other Parse SDKs and setting nil values. Unset works but we do have situations in some cases where values drop to nil without necessarily being explicitly unset.

The NullEncodable property wrapper referenced in that Stack Overflow question looks interesting, seems like it would handle the situation properly as long as optional model properties had this wrapper attached to it. How do you feel about including an implementation of this in the ParseSwift SDK and then including in the documentation instructions on using it for optional values? Realm SDK model objects use property wrappers quite a bit and it's a fairly easily understood process so I don't think that's out of the ordinary, and then it would clarify in the sample model objects that optional values need this wrapper - we weren't aware that the SDK didn't do this already and it caused some bugs when we transitioned from the Objective C SDK to this one on one of our projects.

@cbaker6
Copy link
Contributor

cbaker6 commented Oct 22, 2021

How do you feel about including an implementation of this in the ParseSwift SDK and then including in the documentation instructions on using it for optional values?

maybe, if you are willing to work on a PR with testcases and it doesn’t break the current testcases, I can review it

@cbaker6
Copy link
Contributor

cbaker6 commented Oct 23, 2021

It seems another approach a developer can take is simply add the dependency via SPM, https://github.com/g-mark/NullCodable

and then use the wrapper on your property.

struct TestObject: ParseObject {
	// ParseObject Conformance
	var objectId: String?
	var createdAt: Date?
	var updatedAt: Date?
	var ACL: ParseACL?

        @NullCodable var testValue: String?
  }

var testObj = TestObject()
testObj.testValue = "value" // Set
testObj = try await testObj.save()

testObj.testValue = nil // Unset
testObj = try await testObj.save()

or you can use ParseOperation, #264 (comment)

@Vortec4800
Copy link
Author

So I ran into a problem, and it ties into discussion being had over in #242 and #263.

This is incompatible with emptyObject - when that runs all properties are nil so on save, the encoder tries to unset all properties.

If the work in #263 does end up getting merged, I can likely use that to compare and then only unset properties that were explicitly unset, but I do feel weary about the SDK having implicit persistence. I'll probably jump in on that PR as well. I think some of these features are very important, but a cache might end up being quite fragile.

@cbaker6 cbaker6 added type:improvement and removed type:question Support or code-level question labels Oct 30, 2021
@mtrezza mtrezza added type:feature New feature or improvement of existing feature and removed type:improvement labels Dec 6, 2021
@parse-github-assistant
Copy link

The label type:feature cannot be used in combination with type:improvement.

@parse-github-assistant parse-github-assistant bot removed the type:feature New feature or improvement of existing feature label Dec 6, 2021
@mtrezza mtrezza added the type:feature New feature or improvement of existing feature label Dec 6, 2021
@cbaker6
Copy link
Contributor

cbaker6 commented Jan 20, 2022

Anyone coming across this issue should see my answer here: #268 (comment)

@cbaker6
Copy link
Contributor

cbaker6 commented Jul 13, 2022

Closing this due to no activity and the previous answers providing the capability

@cbaker6 cbaker6 closed this as completed Jul 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type:feature New feature or improvement of existing feature
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants