Click and Collect - Case Study
Some of you tried this service when using apps such as Żappka or Tesco Click and Collect. Most of the apps available in the App Store or the Google Play Store were built natively, so we tried to create our Click and Collect app using React Native to speed up the development process.
Click and Collect - a shopping app that enables you to order things to your local store or directly to your home
Usually when you want to create an app, you should ask some designers for mockups or other design assets. In our case we could use existing ones created by one of our colleagues. You can find his original work on our Dribbble page right here.
According to the designs, our app had to implement the following features:
- You should be able to select your store from the map.
- There should be store-specific news displayed on the main screen.
- After selecting a store, you should be able to browse the products available there.
- After clicking on a product, you should be able to view its details and add it to your basket.
- You should be able to make an order from the basket screen.
Instead of writing about all of the functionalities implemented, take a look at the video that shows what the Click and Collect app can do!
Technologies we used to create it
Sticky-parallax-header
We wanted to make use of our open source project sticky-parallax-header, and we did it! We achieved smooth and nice animations in just a few minutes🎉.
Firebase
As the app was a proof of concept, we didn’t have a proper API or database available to connect to. We decided to create our own DB using Firebase Firestore and create the necessary backend logic using Firebase Functions. As you can see, we will be a bit limited by this choice, but let’s not panic here.
Firestore
We decided to use Firestore because of its speed and efficiency. This decision forced us to create a really specific database architecture to manage stores, user data, orders, and some other things. Usually, when creating an app which uses Firestore or Firebase Realtime DB, we follow a database-first approach. This time we went full-on with the code-first approach as if we were creating some .NET app 😂
In order to do that, we used TypeScript to create types for each document collection. As you probably know, there are some issues when working with React Native Firebase and TypeScript, especially when you want to have generic types of responses, but we will discuss this later.
RxFire
As we’ve mentioned before, when you want to use RN Firebase with TypeScript, there are problems with some types of data fetched from the DB. When we decided to use RxJS in our app, we found a really nice library that tackles type problems. RxFire is a wrapper for Firebase that provides a set of observable creation methods. To use Firebase with RxFire we need to create a queryRef, a docRef, or a collectionRef and pass it to the suitable RxFire method.
There are two very useful methods in that library:
- collectionData - creates an observable that emits a stream of documents for the specified collection;
- docData - creates an observable that returns a stream of a document.
const userRef = firestore().collection('Users').doc(userId);
docData(userRef, 'id').subscribe((userData) => {
basket.next(userData.userBasket);
});
We considered whether we should subscribe to data directly in screens, but finally we decided to make use of these great multidirectional services that are specific to the Angular framework. So we subscribe to a data stream inside the service and then we push the received (and mapped) data to the subject. The last step is just subscribing to those subjects (which are returned as observables) in screens.
RxJS
We decided to use RxJS because we wanted to integrate Firebase with RxFire. The goal was to create services that we can easily switch from Firebase to an API provider. With that solution we were able to switch our backend from Firebase to a ReST API in about one 🚀 day .
When we switched from Firebase to an Axios client we just had to remap our response objects before injecting them into the store.
Our Rx services contain persistent state, so we can restore the saved state after app restart. We rehydrate the state during the splash screen displayed on app launch.
function fetchNews(storeName: string = undefined): void {
const callback = (news: NewsModel[]) => {
setStoreNews(news);
storeNews.next(news);
};
if (env.USE_HTTP) {
http.get<ApiList<DiscountDto>>(url.news(storeName)).subscribe((response) => {
callback(response.values.map<NewsModel[]>(mapStoreNews));
});
return;
}
const newsRef = firestore().collection(`${storeName}_News`);
collectionData<NewsModel>(newsRef, 'id').subscribe(callback);
}
We used the “setStoreNews” method to save data. This function saves data in our persistence service and we are able to read it after the app is launched again.
AR Localizer
During the development we came to a dead end when it comes to AR solutions. We needed to create AR tags that would tell users where the selected store is. We tried the ViroReact solution, but it’s not frequently updated and all examples were made using React Native 0.59. So we had to think about our own solution. Fortunately, Netguru has actually created an AR library for iOS and Android. The only thing we had to do was to bridge it. You can find the original libraries here and here. Our approach was to create native screens with the AR Localizer library and navigate to them from the React Native side.
After the development of the bridge we thought that it would be a great idea to share it with all of you, so we started our next open-source project called react-native-ar-localizer.
Templating system
Before development, we decided to create an app with light and dark modes. To achieve our goal, we used react-native-dynamic. We created two files for the dark and light color palettes. It was a great idea to use a templating system, because we were able to change the app’s branding in a moment.
Simple Admin Panel with Core UI
Thanks to using RxFire, we were able to copy some services to use them in a React.js-based admin panel. The admin panel was just an additional feature to allow us to handle order statuses and adding products and stores. We based it on the CoreUI React.js Admin Template, thanks to which we were able to create a base admin panel very quickly.