Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Saving Data Locally and Remotely (Syncing)

When data is entered, it ultimately needs to be saved remotely on a server. I do want the app to work if there is no data connection at the time also, so I need to save everything locally on the phone too. The app can then sync with the server when it gets a connection.

This brings up a little issue. I'm used to saving everything on the server and then getting the records back with id's generated from the server for them. If there is no connection, the app will save locally to the phone but not the server. When syncing with the server, I don't see a way for the phone to know when a record comes back which locally record it's associated with. There isn't enough unique data to figure this out.

What is the best way to handle this?

One way I've been thinking is to change the id of the records to a GUID and let the phone set the id. This way, all records will have an id locally, and when saving to the server, it should still be a unique id.

I'd like to know what other people have been doing, and what works and what doesn't from experience.

like image 281
Josh Close Avatar asked Dec 06 '25 02:12

Josh Close


2 Answers

This is how we done with a first windows phone 7 app finished few days ago with my friend. It might not be the best solution but 'till additional refactoring it works just fine. It's an application for a web app like a mint.com called slamarica.

If we have feature like save transaction, we first check if we have connection to internet.

        // Check if application is in online or in offline mode 
        if (NetworkDetector.IsOnline)
        {
            // Save through REST API
            _transactionBl.AddTransaction(_currentTransaction);
        }
        else
        {
            // Save to phone database
            SaveTransactionToPhone(_currentTransaction);
        }

If transaction is successfully saved via REST, it responses with transaction object and than we save it to local database. If REST save failed we save data to local database.

 private void OnTransactionSaveCompleted(bool isSuccessful, string message, Transaction savedTransaction)
    {
        MessageBox.Show(message);

        if(isSuccessful)
        {
            // save new transaction to local database
            DatabaseBl.Save(savedTransaction);

            // save to observable collection Transactions in MainViewModel
            App.ViewModel.Transactions.Add(App.ViewModel.TransactionToTransactionViewModel(savedTransaction));
            App.ViewModel.SortTransactionList();

            // Go back to Transaction List
            NavigationService.GoBack();
        }
        else
        {
            // if REST is failed save unsent transaction to Phone database
            SaveTransactionToPhone(_currentTransaction);

            // save to observable collection Transactions in MainViewModel
            App.ViewModel.Transactions.Add(App.ViewModel.TransactionToTransactionViewModel(_currentTransaction));
            App.ViewModel.SortTransactionList();
        }

    }

Every Transaction object has IsInSync property. It is set to false by default until we got confirmation from REST API that it's saved successful on the server.

User has ability to refresh transactions. User can click on a button Refresh to fetch new data from the server. We do the syncing in the background like this:

   private void RefreshTransactions(object sender, RoutedEventArgs e)
    {
        if (NetworkDetector.IsOnline)
        {
            var notSyncTransactions = DatabaseBl.GetData<Transaction>().Where(x => x.IsInSync == false).ToList();


            if(notSyncTransactions.Count > 0)
            {
                // we must Sync all transactions
                _isAllInSync = true;
                _transactionSyncCount = notSyncTransactions.Count;
                _transactionBl.AddTransactionCompleted += OnSyncTransactionCompleted;

                if (_progress == null)
                {
                    _progress = new ProgressIndicator();
                }

                foreach (var notSyncTransaction in notSyncTransactions)
                {
                    _transactionBl.AddTransaction(notSyncTransaction);
                }

                _progress.Show();
            }
            else
            {
                // just refresh transactions
                DoTransactionRefresh();
            }
        }
        else
        {
            MessageBox.Show(ApplicationStrings.NETWORK_OFFLINE);
        }
    }

 private void DoTransactionRefresh()
    {
        if (_progress == null)
        {
            _progress = new ProgressIndicator();
        }

        // after all data is sent do full reload
        App.ViewModel.LoadMore = true;
        App.ViewModel.ShowButton = false;
        ApplicationBl<Transaction>.GetDataLoadingCompleted += OnTransactionsRefreshCompleted;
        ApplicationBl<Transaction>.GetData(0, 10);

        _progress.Show();
    }

OnTransactionRefreshCompleted we delete all transaction data in local database and get the latest 10 transactions. We don't need all the data, and this way user have synced data. He can always load more data by taping load more at the end of transaction list. It's something similar like those twitter apps.

 private void OnTransactionsRefreshCompleted(object entities)
    {
        if (entities is IList<Transaction>)
        {
            // save transactions
            var transactions = (IList<Transaction>)entities;
            DatabaseBl.TruncateTable<Transaction>();
            DatabaseBl.Save(transactions);
            ((MainViewModel) DataContext).Transactions.Clear();
            //reset offset
            _offset = 1;
            //update list with new transactions
            App.ViewModel.LoadDataForTransactions(transactions);

            App.ViewModel.LoadMore = false;
            App.ViewModel.ShowButton = true;
        }
        if (entities == null)
        {
            App.ViewModel.ShowButton = false;
            App.ViewModel.LoadMore = false;
        }

        // hide progress 
        _progress.Hide();

        // remove event handler
        ApplicationBl<Transaction>.GetDataLoadingCompleted -= OnTransactionsRefreshCompleted;
    }
like image 106
nemke Avatar answered Dec 09 '25 18:12

nemke


Caveat - I haven't tried this with windows phone development but use of GUID identities is something I usually do when faced with similar situations - eg creating records when I only have a one-way connection to the database - such as via a message bus or queue.

It works fine, albeit with a minor penalty in record sizes, and can also cause less performant indexes. I suggest you just give it a shot.

like image 34
Chris Ballard Avatar answered Dec 09 '25 18:12

Chris Ballard



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!