Open osehmathias opened 8 months ago
Sorry that you are facing this issue and thanks for reporting it. We will look into this and get back to you when we have updates.
@NikaHsn - thanks, although I think it is a DataStore issue, not a GraphQL issue, as I experience this error with DataStore but not GraphQL.
Hi @osehmathias, I am unable to reproduce this.
.copyWithModelFieldValues()
returns a new instance of the model which needs to be saved. This example works for me.
final entry = Entry(draftRecordID: 'foo');
print('Before: ${entry.draftRecordID}'); // Before: foo
final newEntry = entry.copyWithModelFieldValues(
draftRecordID: const ModelFieldValue.value(null),
);
print('After: ${newEntry.draftRecordID}'); // After: null
Can you confirm how you are calling the copy method?
Also I noticed your schema Entry
should have the @model
decorator. I don't think this is causing issues, but it may have other side effects later on.
type Entry @model {
id: ID!
draftRecordID: ID @index
}
Hi @Equartey
I am calling the copy method like so:
final entry = Entry(draftRecordID: 'foo');
print('Before: ${entry.draftRecordID}'); // Before: foo
final newEntry = entry.copyWithModelFieldValues(
draftRecordID: const ModelFieldValue.value(null),
);
print('After: ${newEntry.draftRecordID}'); // After: null
await Amplify.DataStore.save(newEntry);
// now fetch ....
Entry fetchedEntry = await Amplify.DataStore.query(Entry.classType, where xxxx)
print('Fetched: ${fetchedEntry.draftRecordID}'); // Fetched entry, not cleared
Also I noticed your schema Entry should have the
@model
decorator. I don't think this is causing issues, but it may have other side effects later on.
Thank you. It does have the @model
decorator. I edited it down when I posted the issue.
Hi @osehmathias
My apologies, I cannot reproduce this. I successfully nullified the property when saving before and after.
I tested on both iOS and Android with the same Flutter and Amplify versions. Here is my function & schema:
Future<void> copyTest() async {
final entry = Entry(draftRecordID: 'foo');
print('Before: ${entry.draftRecordID}'); // Before: foo
await Amplify.DataStore.save(entry);
final resBefore = await Amplify.DataStore.query(Entry.classType,
where: Entry.ID.eq(entry.id));
print('resBefore: $resBefore'); // resBefore: Entry.draftRecordID = baz
final newEntry = entry.copyWithModelFieldValues(
draftRecordID: const ModelFieldValue.value(null),
);
print('After: ${newEntry.draftRecordID}'); // After: null
await Amplify.DataStore.save(newEntry);
final resAfter = await Amplify.DataStore.query(Entry.classType,
where: Entry.ID.eq(entry.id));
print('resAfter: ${resAfter}'); // resAfter: Entry.draftRecordID = null
}
type Entry @model {
id: ID!
draftRecordID: ID @index
}
I'm not sure what could be the issue or different about our environments.
Can you confirm the ID's of the entries match?
Is there anything else specific with your setup that would help me reproduce the issue?
Hi @Equartey - I can see in the xcode logs that it sets it to null
"draftRecordID": Amplify.JSONValue.null,
However, when I refetch, the value is still there.
I have been able to get around this by setting the value of draftRecordID
to '-'
instead of null.
Hi @osehmathias. I looked into this again and could not reproduce. Your workaround is reasonable, but understandably not ideal.
Is the same behavior observed on Android?
Are you able to reproduce this on a fresh project?
Sorry about the delay, @Equartey.
This is how it's used.
Entry updatedEntry = currentEntry.copyWithModelFieldValues(
draftRecordID: const ModelFieldValue.value(null));
await ref.read(entriesProvider.notifier).updateEntry(updatedEntry);
In this pattern, the provider updates the DB.
// provider.dart
final entriesProvider =
StateNotifierProvider<EntriesProvider, List<Entry>?>((ref) {
return EntriesProvider(ref);
});
class EntriesProvider extends StateNotifier<List<Entry>?> {
Ref ref;
EntriesProvider(this.ref) : super(null) {
fetchEntries();
}
Future<void> updateEntry(Entry updatedEntry) async {
try {
await Amplify.DataStore.save(updatedEntry);
} catch (e) {
if (kDebugMode) {
print('Error saving GoalEntry to DataStore: $e');
}
return;
}
if (state != null) {
final index = state!.indexWhere((entry) => entry.id == updatedEntry.id);
if (index != -1) {
state = [
for (int i = 0; i < state!.length; i++)
if (i == index) updatedEntry else state![i]
];
}
}
}
}
For this reason, the state updates immediately and the mutation appears to be successful.
However, if I query the object afresh, such as when the app lifecycle changes, which in turn fetches data from the DataStore and updates the state ...
// main.dart
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.resumed) {
var _ = ref.refresh(refreshAllProvider);
initAuthController();
}
}
Or simply by making a call to Amplify.DataStore.query
, or by querying on the AppSync API, or by looking in DynamoDB, I can see that the field has not been set to null and remains with its old value.
However, if I set the value to a string, it will update.
Hi @osehmathias, apologies for the delay.
I was able to reproduce this only on iOS, but not on Android. Are you seeing this issue on Android too?
Still digging into what the cause could be. We'll update you when we have more information to share.
Hi @Equartey
Thank you for the response. I have not tried to replicate this on Android yet, but as it happens, I am just about to develop the same app for Android so I will let you know once I have tested it and have that information.
@osehmathias, understood. Keep up posted. Thanks.
We seem to be having the same issue, except not on a basic type such as a string so the work around wouldn't work for us. It only happens with iOS, we haven't been able to reproduce it on android.
I suppose changing it to a list with only one value and then removing and re-adding to the list would solve it but I would very much like to avoid that.
@filipesbragio thanks for the extra data point.
It appears iOS is dropping properties set to a null
literal when making the request to AppSync. The work around would be to pass a non-null value to represent your type (if possible). If your property is a list, an empty list would also nullify the property. Same with an empty string.
We're currently investigating how to best resolve this and will update you as we can.
Description
I have schema like this
With Amplify GraphQL, I can set draftRecordID to null.
With Amplify DataStore, I can not, even with
entry.copyWithModelFieldValues(draftRecordID: const ModelFieldValue.value(null));
Categories
Steps to Reproduce
Create schema with a nullable String index. Set the String index field to a value. Attempt to remove that value by setting it to
null
.Screenshots
No response
Platforms
Flutter Version
3.19.1
Amplify Flutter Version
1.6.1
Deployment Method
Amplify CLI
Schema