Firebase Realtime Database subscribe fires Without Insert/Update in Xamarin Forms

Viewed 15

I am puzzled by the behavior of the subscribe event in Firebase Realtime Database. I am posting data in firebase which works fine and I am also subscribing to insert/update changes to get the newly added/updated item in the Firebase Realtime Database. However, my subscribe event occurs in any way without inserting/updating anything to the DB.

Here is my MVVM

    public ICommand SaveCommand { get; private set; }
    public ICommand LoadContentsCommand { get; private set; }
    private readonly App CurrentApp;
    private readonly IFirebaseDatabaseService firebaseDatabaseService;
    public ICommand SaveCommand { get; private set; }
    public ICommand LoadContentsCommand { get; private set; }
    private readonly App CurrentApp;
    private readonly IFirebaseDatabaseService firebaseDatabaseService;

    public CommunityHomePageViewModel()
    {
        firebaseDatabaseService = DependencyService.Get<IFirebaseDatabaseService>();
        Title = "Community";

        LoadContentsCommand = new Command(async () => await LoadContentsAsync());
        SaveCommand = new Command(async () => await SaveDataAsync());


        LoadContentsCommand.Execute(null);

        CurrentApp = Application.Current as App;

        SubscribeToChanges();
    }

    ObservableCollection<Post> _items;
    public ObservableCollection<Post> Items
    {
        get { return _items; }
        set { SetProperty(ref _items, value); }
    }
    async Task SaveDataAsync()
    {
        try
        {
            await firebaseDatabaseService.PostAsync(new Post() { Comment = $"Random comment {new Random().Next(0, 100)}", UserKey = CurrentApp.CurrentUser.Id });
        }
        catch (Exception e)
        {
            Logger.LogException(e);
        }
    }

    async Task LoadContentsAsync()
    {
        try
        {
            var items = await firebaseDatabaseService.GetItemsAsync(string.Empty);
            Items = new ObservableCollection<Post>(items);  
        }
        catch (Exception e)
        {
            Logger.LogException(e);
        }
    }
    // subscribe occurs just after the items are loaded without waiting for the insert/save to occur.
    void SubscribeToChanges()
    {
        firebaseDatabaseService.Client().Child(nameof(Post))
            .AsObservable<Post>()
            .Where(post => !Items.Contains(post.Object) && post.EventType == Firebase.Database.Streaming.FirebaseEventType.InsertOrUpdate)
            .Subscribe(obs =>
            {
                Console.WriteLine($"{obs.Key}");
                var post = obs.Object;
                switch (obs.EventType)
                {
                    case Firebase.Database.Streaming.FirebaseEventType.InsertOrUpdate:
                        Items.Insert(0, post);
                        break;
                }
            });
    } 

and this is the Get method

    async Task<IEnumerable<Post>> IFirebaseDatabaseService.GetItemsAsync(string id)
    {
        if (!_firebaseAuth.IsSignedIn) return null;

        if (!string.IsNullOrEmpty(id))
        {
            return (await _firebaseClient
               .Child(typeof(Post).Name)
               .OrderByKey()
               .StartAt(id)
               .LimitToFirst(10)
               .OnceAsync<Post>())
               .Select(i => new Post
               {
                    Comment = i.Object.Comment,
                    UserKey = i.Object.UserKey
               });
        }

        return (await _firebaseClient
               .Child(typeof(Post).Name)
               .OrderByKey()
               .LimitToFirst(10)
               .OnceAsync<Post>())
               .Select(i => new Post
               {
                   Comment = i.Object.Comment,
                   UserKey = i.Object.UserKey
               });
    }

Why would the subscribe event even fire before the insert? Am I following the wrong pattern?

1 Answers

I've never used the InsertOrUpdate event type on the step-up-labs/firebase-database-dotnet library, but I'm guessing it wraps Firebase's native child_added and child_changed events.

In that case, the fact that it fires without any changes is expected, as the Firebase documentation on listening to child events says:

child_added:

Retrieve lists of items or listen for additions to a list of items. This event is triggered once for each existing child and then again every time a new child is added to the specified path.

So when you first attaches the listener, it fires child_added for each existing child node.

If you only want to get new updates after you attach the listener, you can add a timestamp to each child node, and then order/filter on that to start listening after the current time.

Related