Fetched dummy objects override cached data

Hey there,

I’m using the Parse iOS SDK 1.19.2. I have a problem with dummy data returned from Cloud Code functions overwriting previously fetched data:

My data model contains a parent-child relationship (the child stores a reference to the parent). I use a CC function to query the parent object with all its properties (name etc.); later I use another CC function to create a new child object. The returned child object only contains a dummy parent object (i.e. only has the id, but no other properties). This dummy object overwrites the “real” parent object I previously fetched with the 1st function.

I double-checked this observation by explicitly using include in the 2nd CC function to include the entire parent object, and sure enough this avoids the problem on the client – the cached object now keeps its data.

Now my question is whether this is intended behavior, and if it is, how I can disable it.

Thanks!


P.S.: In the following, I’ll outline my exact setup (maybe the simplified example above lacks something important):

  1. I use a CC function to find and return a Project object. I store this object in an in-memory cache.

  2. After that, I use another CC function to create and return a new Task object. Task objects have an Element, and Element objects have a Project, so you could look up the Project containing a Task by task.element.project.

The 2nd CC function’s response Task fully includes the Element property, but this in turn does not include the nested Project; so task.element is the full Element object, but task.element.project is a just dummy Project object.

Th outcome of this is that after the 2nd CC function has returned, the cached Project object has lost all its data; e.g. project.name contained the project name after the 1st call, but is nil after the 2nd call.

Would you mind to share the code? It might be easier to understand and help.

Hey there,

thanks for answering.

What aspect of the description is unclear? TBH I don’t think that pasting all the relevant code will make anything simpler… The whole scenario involves the data structures, two CC functions, calling these functions from iOS, and caching the results. That’s a lot of code, but the problem is not about the code per se; rather I’d like to understand why the API behaves as it does. So if you tell me which aspect of the scenario I described requires more details, I’ll fill out the blanks.

Best regards,
Martin

If you can share a minimum code which reproduces the problem, it is might easier to understand. Even better if you can submit a failing test case to the repository. Basically it is much easier to understand and fix the problem seeing the code than trying to understand a text description.

Of what I can understand so far, it looks something related to the way you are returning the project object from the 2nd cc function. But it is not clear to me what you are trying to do, how you are doing that, and what you mean by dummy Project object.

Ok, let’s give it a try then :slight_smile:

Here’s a simplified version of the code. I hope this helps to understand the issue I tried to describe. (I have no idea how to provide a failing test case since the issue only surfaces within the iOS SDK – how to construct a test which covers both CloudCode and iOS?)

Data model

Task has an Element, and Element has a Project, so:

Project (1) <-- (*) Element (1) <-- (*) Task  

This chain of relationship also is reflected in the app: the user selects one from a list of projects, then sees the elements of that project, and can create a task for one of the elements.

Backend functions

getProjects just runs a query and returns an array of objects:

const parents = await new Parse.Query('Project').find({ useMasterKey: true })
res.success(parents)

createTask creates a new task for a provided element. It queries the Element object, creates the new Task object with that element, and returns the task:

const element = await new Query('Element').get(elementId, { useMasterKey: true })
const task = new Task({ element, /* ... some other arguments */ })
await task.save(null, { useMasterKey: true })
res.success(task)

The task returned by createTask contains the full element object (we fetched it in createTask), but this element object in turn doesn’t contain the full project object (since we didn’t use include('project') in the element query); it just contains the reference to the project (that’s what I meant with “dummy object”, similar to what would be returned by createWithoutData): task.get('element').get('project') returns a Parse object which only has its id set, but none of the other properties of the Project class.

iOS app

The iOS app first calls getProjects and stores the returned objects in a variable (no disk-based caching involved, just a simple in-memory array).

After that, the app performs some more data fetching, getting the elements of the projects etc.

When all data is loaded, the user can select a project, then select an element in this project, and finally create a task for that element. The app calls createTask and passes an elementId as parameter. The CC function creates the new task and returns it.

Problem & Hotfix

The element reference in the returned task object contains the aforementioned “dummy” project object (task.element.project). The SDK apparently sees that the returned project object already has been fetched before, so it decides to update the existing in-memory instance. This overwrites the previoulsy fetched Project instance (which I got from getProjects) with the “dummy” instance which was returned by createTask, so from now on all accesses to this project’s properties will return nil.

If I simply add include('project') to the element query in createTask, the issue is gone since now the same project data is retransmitted and no local data is lost (I guess it’s still overwritten, but with the same values). However, I can’t believe that this behaviour is by design: I would always have to return the entire object tree to the app to avoid cached data being overwritten.

I got the problem and I believe it shouldn’t happen since element.get('project') should actually return a pointer to the Project object and not the object itself. It being a pointer, the client cache should not update. I believe it is either something related to your cloud code function or a bug in the sdk.