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

Able to write POCO to Firebase Firestore but not able to read back #422

Open
dvillavicencio-mfe opened this issue Feb 27, 2025 · 0 comments

Comments

@dvillavicencio-mfe
Copy link

dvillavicencio-mfe commented Feb 27, 2025

Hi, I have a small issue with my .NET Maui app whilst writing and reading back from Firestore for cloud syncing. I have the following POCOs:

CrewCheckInObject (top-most object in the hierarchy)

public class CrewCheckIn : IFirestoreObject
{
    public CrewCheckIn()
    {
    }

    public CrewCheckIn(List<CrewCheckInEmployee> employees, List<CrewCheckInAsset> yardAssets, string clockInTime, string yardLocation, bool emergencyCheckIn, List<CrewCheckInRemovedAsset> removedAssets, List<CrewCheckInLog> logEntries, DateTime timestamp)
    {
        Employees = employees;
        YardAssets = yardAssets;
        ClockInTime = clockInTime;
        YardLocation = yardLocation;
        EmergencyCheckIn = emergencyCheckIn;
        RemovedAssets = removedAssets;
        LogEntries = logEntries;
        Timestamp = timestamp;
    }

    [FirestoreProperty("employees")]
    public IList<CrewCheckInEmployee> Employees { get; set; }

    [FirestoreProperty("yardAssets")]
    public IList<CrewCheckInAsset> YardAssets { get; set; }

    [FirestoreProperty("clockInTime")]
    public string ClockInTime { get; set; }

    [FirestoreProperty("yardLocation")]
    public string YardLocation { get; set; }

    [FirestoreProperty("emergencyCheckIn")]
    public bool EmergencyCheckIn { get; set; }

    [FirestoreProperty("removedAssets")]
    public IList<CrewCheckInRemovedAsset> RemovedAssets { get; set; }

    [FirestoreProperty("logs")]
    public IList<CrewCheckInLog> LogEntries { get; set; }

    [FirestoreProperty("timestamp")]
    public DateTime Timestamp { get; set; }
}

CrewCheckInEmployee

public class CrewCheckInEmployee : IFirestoreObject
{

    public CrewCheckInEmployee() { }

    public CrewCheckInEmployee(string name, string clazz, int crew, List<string> languages, List<CrewCheckInAsset> assignedEquipment, List<CrewCheckInAsset> assignedVehicles, string clockInTime, string status, string workType, List<string> jobNumbers, string notes)
    {
        Name = name;
        Clazz = clazz;
        Crew = crew;
        Languages = languages;
        AssignedEquipment = assignedEquipment;
        AssignedVehicles = assignedVehicles;
        ClockInTime = clockInTime;
        Status = status;
        WorkType = workType;
        JobNumbers = jobNumbers;
        Notes = notes;
    }

    [FirestoreProperty("name")]
    public string Name { get; set; }

    [FirestoreProperty("class")]
    public string Clazz { get; set; }

    [FirestoreProperty("crew")]
    public int Crew { get; set; }

    [FirestoreProperty("languages")]
    public IList<string> Languages { get; set; }

    [FirestoreProperty("assignedEquipment")]
    public IList<CrewCheckInAsset> AssignedEquipment { get; set; }

    [FirestoreProperty("assignedVehicles")]
    public IList<CrewCheckInAsset> AssignedVehicles { get; set; }

    [FirestoreProperty("clockInTime")]
    public string ClockInTime { get; set; }

    [FirestoreProperty("status")]
    public string Status { get; set; }

    [FirestoreProperty("workType")]
    public string WorkType { get; set; }

    [FirestoreProperty("jobNumbers")]
    public IList<string> JobNumbers { get; set; }

    [FirestoreProperty("notes")]
    public string Notes { get; set; }
}

Crew Check In Log

public class CrewCheckInRemovedAsset : IFirestoreObject
{

    public CrewCheckInRemovedAsset() { }

    public CrewCheckInRemovedAsset(string assetName, string assetDescription, string reason)
    {
        AssetName = assetName;
        AssetDescription = assetDescription;
        Reason = reason;
    }

    [FirestoreProperty("assetName")]
    public string AssetName { get; private set; }

    [FirestoreProperty("assetDescription")]
    public string AssetDescription { get; private set; }

    [FirestoreProperty("reason")]
    public string Reason { get; private set; }
}

Crew Check In Log

public class CrewCheckInLog : IFirestoreObject
{
    public CrewCheckInLog() { }

    public CrewCheckInLog(DateTime timestamp, string action, string message)
    {
        Timestamp = timestamp;
        Action = action;
        Message = message;
    }

    [FirestoreProperty("timestamp")]
    public DateTime Timestamp { get; set; } // Plugin.Firebase maps to Firestore timestamp

    [FirestoreProperty("action")]
    public string Action { get; set; }

    [FirestoreProperty("message")]
    public string Message { get; set; }
}

Crew Check In Asset

public class CrewCheckInAsset : IFirestoreObject
{
    public CrewCheckInAsset() { }

    public CrewCheckInAsset(string description, string name, string @operator, string type)
    {
        Description = description;
        Name = name;
        Operator = @operator;
        Type = type;
    }

    [FirestoreProperty("description")]
    public string Description { get; set; }

    [FirestoreProperty("name")]
    public string Name { get; set; }

    [FirestoreProperty("operator")]
    public string Operator { get; set; }

    [FirestoreProperty("type")]
    public string Type { get; set;  }
}

I'm able to put this into Firestore correctly using the following snippet of code:

public async Task Sync(CrewCheckIn crewCheckIn)
{
    var document = _firebaseFirestore.GetDocument($"UserData/{Authentication.userUID}");
    await document.SetDataAsync(crewCheckIn);
}

however, when it comes to reading it back in the following snippet:

private partial async Task<CrewCheckIn> GetCrewCheckInImpl()
{
    var document = _firebaseFirestore.GetDocument($"UserData/{Authentication.userUID}");
    var snapshot = await document.GetDocumentSnapshotAsync<CrewCheckIn>();
    return snapshot.Data;
}

I get an NSObject exception that isn't being thrown when reading the .Data member of the snapshot object. I'm also testing this on iOS. I just wanted some clarification if something in my code is wrong that I don't see it or if its a deeper issue than what I think.

Any help is appreciated, thanks!

Edit: I'm adding the entire POCO structure and (hopefully) a useful stack trace. I didn't want to add as much because I don't want to code dump but I figured I would so we can see the issue at hand better.

Stack Trace when program crashes:

2025-02-27 14:48:02.625 Xamarin.PreBuilt.iOS[1410:418710] Plugin.Firebase.Firestore.Platforms.iOS.DocumentSnapshotWrapper`1[RGIHome.Dtos.CrewCheckIn]
Thread started: <Thread Pool> #53
[0:] An error occurred: 'Could not convert NSObject of type Foundation.NSObject to object'. Callstack: '   at Plugin.Firebase.Firestore.Platforms.iOS.Extensions.NSObjectExtensions.ToObject(NSObject this, Type targetType)
   at Plugin.Firebase.Firestore.Platforms.iOS.Extensions.NSObjectExtensions.Cast(NSDictionary this, Type targetType, String documentId)
   at Plugin.Firebase.Firestore.Platforms.iOS.Extensions.DictionaryExtensions.ToDictionaryObject(NSDictionary this, Type targetType)
   at Plugin.Firebase.Firestore.Platforms.iOS.Extensions.NSObjectExtensions.ToObject(NSObject this, Type targetType)
   at Plugin.Firebase.Firestore.Platforms.iOS.Extensions.ListExtensions.ToList(NSArray this, Type targetType)
   at Plugin.Firebase.Firestore.Platforms.iOS.Extensions.NSObjectExtensions.ToObject(NSObject this, Type targetType)
   at Plugin.Firebase.Firestore.Platforms.iOS.Extensions.NSObjectExtensions.Cast(NSDictionary this, Type targetType, String documentId)
   at Plugin.Firebase.Firestore.Platforms.iOS.Extensions.NSObjectExtensions.Cast[CrewCheckIn](NSDictionary this, String documentId)
   at Plugin.Firebase.Firestore.Platforms.iOS.DocumentSnapshotWrapper`1[[RGIHome.Dtos.CrewCheckIn, RGIHome, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].get_Data()
   at RGIHome.Repository.CrewCheckInRepository.GetCrewCheckIn() in C:\Users\Daniel\Source\Repos\RGIHome\RGIHome\Repository\CrewCheckInRepository.cs:line 76
   at RGIHome.Services.CheckInStateManager.SaveStateAsync() in C:\Users\Daniel\Source\Repos\RGIHome\RGIHome\Services\CheckInStateManagerService.cs:line 148
   at RGIHome.Services.CheckInViewModel.BackButton() in C:\Users\Daniel\Source\Repos\RGIHome\RGIHome\ViewModels\CheckInPage\CheckInViewModel.cs:line 118
   at CommunityToolkit.Mvvm.Input.AsyncRelayCommand.AwaitAndThrowIfFailed(Task executionTask)
   at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_0(Object state)
   at Foundation.NSAsyncSynchronizationContextDispatcher.Apply() in /Users/builder/azdo/_work/1/s/xamarin-macios/src/Foundation/NSAction.cs:line 179
   at UIKit.UIApplication.UIApplicationMain(Int32 argc, String[] argv, IntPtr principalClassName, IntPtr delegateClassName) in /Users/builder/azdo/_work/1/s/xamarin-macios/src/UIKit/UIApplication.cs:line 61
   at UIKit.UIApplication.Main(String[] args, Type principalClass, Type delegateClass) in /Users/builder/azdo/_work/1/s/xamarin-macios/src/UIKit/UIApplication.cs:line 96
   at RGIHome.Program.Main(String[] args) in C:\Users\Daniel\Source\Repos\RGIHome\RGIHome\Platforms\iOS\Program.cs:line 13
   at System.Object.InvokeStub_Program.Main(Object , Span`1 )
   at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)'
The app has been terminated.The app has been terminated.

Edit #2: This code works on android, its an iOS issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant