A few days ago I decided to cook some paella. I found a recipe online, compiled a list of ingredients, and visited a couple of stores to buy the ingredients – not a simple algorithm. Well, this process could have been way easier had I used a mobile app we recently developed for a large supermarket chain. Besides its benefits for the household, which I'll discuss later, the project really helped us grow professionally, gaining new development skills. Namely, we dealt with mobile drag-and-drop features, debugging the data synchronization for working with third-party API, and rethinking the overall domain. If you’re an iOS or Android developer working on similar projects, this post is for you.
Project Scope: Meal Planning App
First, let me tell you about the project and the app’s functionality. We had to develop two version of a mobile application, for iOS and Android platforms. Our client’s idea was to inform users about the supermarket’s marketing campaigns (sales, special offers, etc. ) all while providing shoppers with a helpful meal-planning tool.
The app includes a regularly updated gallery of dishes together with recipes, ingredients to which can be found on sale at the supermarket. Users can plan the menu and add the recipes they liked to a calendar. Based on the menu they have created, the app calculates how much of each ingredient is needed and provides users with a complete list of things to buy. In other words, the user doesn’t need to worry about what and where to buy: all ingredients can be purchased within the supermarket at special prices. Sounds like a great idea, but to make it happen we had to solve several issues.
While working on the app we faced some challenges with image transferring from the image gallery to the calendar.
In Android applications, in order to grab an element and drag it elsewhere, you would use the “long click”. The long click is processed by the gallery, which also handles other touch events. Therefore, if the user grabs the image from the gallery and drags it downward to the calendar, the gallery might start scrolling. We wanted to avoid that.
To make drag-and-drop work correctly, we moved touch event processing from the gallery into a global coordinate system. Thereby we managed to avoid gallery scrolling and organized the interaction between the transferred elements and other objects on display.
A similar problem appeared when developing the iOS version of the application. In iOS version, TableView interpreted a downward gesture as a scroll. To correct the situation we made a decision to disable TableView's gesture recognizers as soon as user touches the image that needs to be moved to the calendar.
Data updates and synchronization
A more complicated issue was faced when implementing a mechanism for updating data and synchronizing it with the server.
First of all, we moved these processes to a separate background service so they don’t interfere with whatever the user is doing.
So, what were the problems?
First, one of the requirements stated that the app should be fully-functional while working offline. So, all data (including special offers, recipes, shopping lists, etc.) should be stored not only in the server, but also in the app itself, so users could access it even offline. As soon as an Internet connection appears, a process of synchronizing previously saved data with new data begins.
Of course, all data should be correctly presented on display while updating. However, the problem is that the application consists of a large number of related entities, whose link could be easily broken during the process of updating.
As a way to solve these problems we employed data caching and also decided to synchronize related code sections between work flows.
In addition, we had to devote special attention to the calendar, since there were some addition requirements. First, we had to associate the calendar with a user account, so that family members could simultaneously plan the menu from different devices, either through a mobile app or a website.
This being said, the process of data synchronization in the calendar should start only after successful update of the domain. Thereby we could avoid receiving unknown recipes from server.
Usually, synchronization can be achieved rather easily by saving the dates of changes and then comparing, whose changes are the most recent. However, in our case we encountered some difficulties with third-party API.
One option was to organize synchronization using only the lastModifiedDate field, where we could save the time of last change to an element on the server. But this wasn’t enough to calculate the time difference and compare the dates of the last changes on device and on server. That's why we added current server time to API.
Still, the API wasn’t working properly. It returned the server time in UNIX-time format and the lastModifiedDate field as following: “dd.MM.yyyy HH:mm”.
The main pitfall was that UNIX-time format includes seconds, whereas lastModifiedDate does not. This means that if changes occurred on a mobile device and on the server within one minute, the result of synchronization could become unpredictable.
Furthermore, UNIX-time format doesn’t include information about time zones. On the other hand, formatting “dd.MM.yyyy HH:mm” took into account the server’s time zone -- GMT+2 and time zone of the device while decoding it. We can hardly guess the results of synchronization under such conditions.
In the end, we were finally able to get the API to work properly and the field lastModifiedDate was returned in UNIX-time, including seconds, milliseconds, and without taking time zones into consideration. This way, we solved all the problems and we able to deliver the app to our client.
With over 100,000 downloads from App Store and Google Play, and thousands of users having delicious meals for dinner, the resulting solution could certainly be considered a success.