How do I retrieve data from Firebase into Unity3d via JSON? - unity3d

I hope I'm going to make my question as clear as possible.
Basically
I have a list of hundreds of items saved in my FirebaseDatabse.
I convert the data to a JSON
Now I want to use that JSON and apply all the data into different items in the unity scene
I have tried to read all nodes/items in my firebase child section, and I also declared 'foreach' method to do something with each child it faces. But it does not work when I want to call another class or function inside the 'foreach' method and I have no idea why it does not!
I really appreciate any help or idea to achieve it.
If possible I want to make a 'foreach' method and deal with each item differently because the key is different so does the value of each item present in the JSON.
Player.Child(UserId).Child("User_Inventory").Child("Items").GetValueAsync().ContinueWith(task => {
if (task.IsFaulted)
{
// Handle the error...
Debug.LogError("Could not finish (LoadPlayerData) !!");
}
else if (task.IsCompleted)
{
DataSnapshot snapshot = task.Result;
// here is the data for all the items that I have
string JSON = snapshot.GetRawJsonValue();
// Now How can I deal with each key/item present in the JSON?
```
[Update]
public void RetrieveData () {
Player.Child(UserId)
.Child("User_Inventory").GetValueAsync().ContinueWith(task => {
if (task.IsFaulted)
{
// Handle the error...
Debug.LogError("Could not finish (LoadPlayerData) !!");
}
else if (task.IsCompleted)
{
DataSnapshot snapshot = task.Result;
foreach (DataSnapshot child in snapshot.Children)
{
print(Child); // shows only one child's data when the below
//line of code is present!!
DisplayInfo(Child.Key, (int)Child.Value);
}
}});
}
public GameObject Item;
public GameObject Contents;
public void DisplayInfo(string Name, int quantity)
{
Item = Contents.transform.parent.Find(Name).GetComponent<QTY_UI>();
Item.Qty = quantity; // just passing quantity to another class
}

Rather than convert your firebase data to a RawJson and trying to iterate the JSON object, you can iterate through the children of the snapshot directly. You can iterate through children of children in the same manner.
if(task.IsCompleted)
{
DataSnapshot snapshot = task.Result;
foreach (DataSnapshot child in snapshot.Children)
{
string key = child.Key;
string value = child.Value;
//Process child here.
}
}
Update:
Try this to get your children in a more manageable format
Dictionary<string, object> dict = new Dictionary<string, object>();
foreach (DataSnapshot child in snapshot.Children)
{
dict.Add(child.Key, child.Value);
}
//dict now contains all of your json children as a dictionary reference.

Related

.NET MAUI: Passing objects from ObservableCollection to an "AddObject" page and back (MVVM)

My main page has an ObservableCollection (defined in a ViewModel) holding objects (defined in a Model) and I have other pages, one that shows details and one to add a new object. I use the MVVM CommunityToolkit.
I can pass one object from the collection to the details page:
[RelayCommand]
async Task GoToDetails(DailyScrum scrum)
{
if (scrum is null)
return;
await Shell.Current.GoToAsync($"{nameof(View.ScrumDetailsPage)}", true,
new Dictionary<string, object>
{
{"Scrum", scrum }
});
}
However, I am not able to pass the whole collection to the "Add" page, add the new object and pass it back:
[RelayCommand]
async Task AddNewScrum(ObservableCollection<DailyScrum> scrums)
{
if (scrums is null)
return;
await Shell.Current.GoToAsync($"{nameof(View.AddScrumPage)}", true,
new Dictionary<string, object>
{
{"Scrums", scrums }
});
}
How can I do this? Or is it a wrong attempt to pass around the collection? Can I write access the collection from another viewmodel?
If the ObservableCollection's size is small, you can try to use the Newtonsoft.Json package to convert it to string and then pass it. Such as:
var jsonstring = JsonConvert.SerializeObject(scrums);
Shell.Current.GoToAsync($"{nameof(View.AddScrumPage)}?param={jsonstring}");
And convert the string to ObservableCollection with the following code:
ObservableCollection<DailyScrum> data = JsonConvert.DeserializeObject<ObservableCollection<DailyScrum>>(jsonstring);
If the collection's size is big, you can store the string in the Preferences. Such as:
var jsonstring = JsonConvert.SerializeObject(scrums);
Preferences.Set("Data", jsonstring);
And get the data in the AddScrumPage:
bool hasKey = Preferences.ContainsKey("Data");
var content = Preferences.Get("Data", string.Empty);
ObservableCollection<DailyScrum> data = hasKey ? JsonConvert.DeserializeObject<Model>(content) : null;
Update
You can try to use the Shell.Current.Navigation, it has the same effect as the Shell.Current.GoToAsync, such as:
[RelayCommand]
async Task GoToDetails(DailyScrum scrum)
{
if (scrum is null)
return;
await Shell.Current.Navigation.PushAsync(new View.ScrumDetailsPage(scrums))
}

For loop doesn't iterate through the whole variable, instead it only performs the functionality only for the first value. Why?

I'm trying to delete the todos in the Map data-type (todoDate) I have created.
the Map<String, List> data type variable problem (todoDate) problem:
I want every key in the Map to be deleted but instead what happens is that only the key with index 0 is deleted and the rest remain. Thanks for helping.
String uid = FirebaseAuth.instance.currentUser!.uid.toString();
final todo =
FirebaseFirestore.instance.collection('users').doc(uid).collection('todos');
void Function()? deleteTodoCollection(Map<String, List<String>> todoDate) {
//delete from firebase
todo.get().then((snapshot) {
for (DocumentSnapshot doc in snapshot.docs) {
doc.reference.delete();
}
});
//delete from the todoDate map
for (var key in todoDate.keys) {
todoDate.remove(key);
print(todoDate);
}
return null;
}
try toList()
...
for (var key in todoDate.keys.toList()) {
...

Dart list added to another list becomes empty when emptying the first list

I have the below user defined objects -
class PhoneBookContact {
String? phoneBookContact;
List<ContactNumber>? contactNumbers;
PhoneBookContact(this.phoneBookContact, this.contactNumbers);
}
class ContactNumber {
String phone;
bool availableOnBol;
ContactNumber(this.phone, this.availableOnBol);
}
In my main method I am creating a list for the ContactNumber class and then later adding that ContactNumber list to the contactNumbers property of PhoneBookContact list. This is all done inside of a for loop.
The issue I am having is when I am clearing the contactNumbers list after adding those items to the contactNumbers property of the PhoneBookContact list, I see those cleared from the PhoneBookContact list as well, which I find weird, or maybe I am not thinking it the right way.
List<PhoneBookContact> phoneBookContacts = [];
List<ContactNumber> contactNumbers = [];
for (var contact in contacts) {
contactNumbers.clear();
if (contact.phones.isNotEmpty) {
for (var phone in contact.phones) {
if (true) {
contactNumbers.add(ContactNumber(phone.number, true));
} else {
contactNumbers.add(ContactNumber(phone.number, false));
}
}
}
phoneBookContacts
.add(PhoneBookContact(contact.displayName, contactNumbers));
}
Your PhoneBookContact constructor does not make a copy of the list. It takes a reference to the list and remembers it.
Since you clear the list every loop but reuse the same list instance, all your PhoneBookContacts will have the same list.
Lets clean up your method a little:
List<PhoneBookContact> phoneBookContacts = [];
for (var contact in contacts) {
List<ContactNumber> contactNumbers = [];
for (var phone in contact.phones) {
final condition = true; // should be your complicated expression
contactNumbers.add(ContactNumber(phone.number, condition));
}
phoneBookContacts.add(PhoneBookContact(contact.displayName, contactNumbers));
}
Apart from removing some clutter, it makes sure that each loop, you instantiate a new list so that each PhoneBookContact has it's own list.

Duplicate Array list value automatically changed in Flutter

I'm very surprised after this issue. First, inform all things I have used in my project.
I have used Getx in my project. I have called the API using the Getx controller file.
Below code used in getx controller file. "PLTimeSlotModel" is model and. it has two params (name, isselect).
var futureTimeSlot_PL_C_D = Future.value(<PLTimeSlotModel>[]).obs;
callTimeSlotAPI() async {
futureTimeSlot_PL_C_D.value = FetchTimeSlotList();
}
Future<List<PLTimeSlotModel>> FetchTimeSlotList() async {
// Fetching data with API calling
}
Screen A:
List<PLTimeSlotModel> listA = [];
List<PLTimeSlotModel> listB = [];
#override
void initState() {
super.initState();
_plController.callTimeSlotAPI();
}
Another method is to create two lists using the future list:
List<PLTimeSlotModel> temp1 = await _plController.futureTimeSlot_PL_C_D.value;
temp1.forEach((element) {
listA.add(element);
listB.add(element);
});
onclick:
for(int i =0;i<listA.length;i++){
listA[i].isselect = false;
print(listA[i].isselect);
print(listB[i].isselect);
}
Now the issue is I have changed/updated the only "listA" value, So why automatically set the same value to "listB"? The two list is based on the one list.
A List in Dart contains references objects, not the objects themselves. So what you are doing is copying references to objects into two lists. But since they are pointing at the same objects, you will see any modification in one list also happen in the other list. You need to copy each PLTimeSlotModel object and put the copy into your new list.
One way to copy objects is to create an constructor which takes an object of the same type and creates a new object based on this first object. So something like this:
class Person {
String name;
Person(this.name);
Person.fromPerson(Person other) : this(other.name);
#override
String toString() => 'Person($name)';
}
void main() {
final persons = [
Person('Adam'),
Person('Bob'),
];
// Make new list based on persons
final otherPersonList = [
...persons.map((person) => Person.fromPerson(person))
];
otherPersonList[0].name = 'Carl';
print(persons); // [Person(Adam), Person(Bob)]
print(otherPersonList); // [Person(Carl), Person(Bob)]
}

GWT Sorting after filtering a table

I have a simple table (CellTable) and a TextBox for filter.
Values for CellTable I take from backend. After list of items is loaded I can put some filter string to get suitable result. To filter list from dataprovider I use a predicate. After filtering I set result to CellTable.
Like here:
void onFilterNameKeyUp(KeyUpEvent e) {
List<CustomerDTO> filtered = this.dataProvider.getList();
Predicate<CustomerDTO> filter = new Predicate<CustomerDTO>() {
#Override
public boolean apply(CustomerDTO input) {
if (input.getName() == null) {
return false;
} else {
return input.getName().toLowerCase()
.contains(filterName.getValue().toLowerCase());
}
}
};
filtered = Lists.newArrayList(Iterables.filter(this.dataProvider.getList(), filter));
this.customerList.setRowCount(filtered.size(), true);
this.customerList.setRowData(0, filtered);
}
this.dataProvider = new ListDataProvider<CustomerDTO>();
dataProvider.addDataDisplay(this.customerList);
final ListHandler<CustomerDTO> sortHandler = new ListHandler<CustomerDTO>(
this.dataProvider.getList());
this.customerList.addColumnSortHandler(sortHandler);
this.customerList.getColumnSortList().push(this.rbKeyNameColumn);
Everything work fine but I have also a sort handler for first column of this table and when I want to sort a list then I'm sorting original list of items.
How to solve this problem? It means that I want to sort and display filtered list instead of original list from dataprovider.
Please help.
I have a table changing content after receiving some data from the server. This is my approach:
public void onSuccess(List<List<String>> result) {
List<Product> list = dataProvider.getList();
list.clear();
r_tableproducts.setRowCount(result.size());
for (List<String> l : result) {
list.add(new Product(l));
}
r_tableproducts.getColumnSortList().clear();
r_tableproducts.getColumnSortList().push(r_tableproducts.getColumn(1));
ColumnSortEvent.fire(r_tableproducts,r_tableproducts.getColumnSortList());
}
This sorts my list using 2nd column after reciving new data (overriding all old data).
Maybe try this approach? Filter your data in a new list and modify the list your dataProvider provides you, adding all elements.
Something like:
List<CustomerDTO> result = Lists.newArrayList(Iterables.filter(this.dataProvider.getList(), filter));
this.customerList.setRowCount(result.size(), true);
this.dataProvider.getList().clear();
this.dataProvider.getList().addAll(result);
You need to separate the dataProvider that holds the full list and provide a separate "filtered" data provider that will be driving the display of the cellTable. The cellTable in below example is bound to a filteredDataProvider for sorting, display purposes.
The code below is to give a general idea and therefore may not be syntactically accurate.
void onFilterNameKeyUp(KeyUpEvent e) {
List<CustomerDTO> filtered = this.dataProvider.getList();
Predicate<CustomerDTO> filter = new Predicate<CustomerDTO>() {
#Override
public boolean apply(CustomerDTO input) {
if (input.getName() == null) {
return false;
} else {
return input.getName().toLowerCase()
.contains(filterName.getValue().toLowerCase());
}
}
};
filtered = Lists.newArrayList(Iterables.filter(this.dataProvider.getList(), filter));
this.customerList.setRowCount(filtered.size(), true);
this.filteredDataProvider.getList().clear();
this.filteredDataProvider.getList().addAll(filtered);
}
this.dataProvider = new ListDataProvider<CustomerDTO>();
// Code here to populate the dataProvider
this.filteredDataProvider = new ListDataProvider<CustomerDTO>();
// Creating separate DataProvider to hold the filtered set
this.filteredDataProvider.getList().addAll(this.dataProvider.getList());
filteredDataProvider.addDataDisplay(this.customerList);
// dataProvider.addDataDisplay(this.customerList);
// Sort Handler uses filteredDataProvider instead of the dataProvider with full list
final ListHandler<CustomerDTO> sortHandler = new ListHandler<CustomerDTO>(
this.filteredDataProvider.getList());
this.customerList.addColumnSortHandler(sortHandler);
this.customerList.getColumnSortList().push(this.rbKeyNameColumn);