I made: POS with React Native
Tags: reactnative, android, showdev
Published: 2017-09-16 12:55:03

---
title: "I made: POS with React Native"
published: true
description: Tale of a React Native POS app part I.
tags: react-native, android, showdev
---

This is part I of a series of posts I'll write explaining the progress of the development of a custom made POS app for Android using React Native. 

## Context

My wife just established a Neko Cafe where people gather to enjoy some coffee while cats are around playing and that kind of stuff. There's also a store where clients can buy cat related gear like pendants, t-shirts, etc.

After studying the different options for a POS on the market, I decided to do one of my own. A side project that took close to 200 hours of work during weekends and days off and is still on development while we add new features.

![Features](https://thepracticaldev.s3.amazonaws.com/i/66r65oc2q37oatv3ro2e.png)

So, the main requirements were:

* Run on an Android tablet (Galaxy Tab A10)
* Print to a Bluetooth printer.
* Track time clients are with the cats (one hour, half hour, etc).
* Track number of clients on each ticket.
* Track money on the drawer with an open/close day flow.
* Take reservations.
* Allow to add one time products to a ticket.
* Manage long term products (coffee, tea, mugs, etc).

## Pre-study

Of course, who would be crazy enough to write their own POS when there are many out there. Well, first, we tried to buy a whole solution from different companies but those were over budget and we were worried several of our requirements wouldn't be met. So we went to Amazon[1], and bought the Bluetooth printer and cash drawer for close to 100â‚¬. Since we already had several tablets at home we saved 200â‚¬ right there. Those Windows based POS with touchscreen are quite expensive.

I still didn't want to mess up doing a POS so off to the Google Play Store. There are many great apps which do the job, for example Loyverse. There were many others that, lets just say were so ugly and complicated that would take me more time than doing the app by myself. At the end, none of them matched our requirements, so off to the drawing board.

## React Native

This is not my first Android or React Native app, so I knew what I was getting into and my major concern was with the Bluetooth printer. Luckily the one we got came with the source code for iOS, Android, Windows and Linux.

So, with most of the Bluetooth code there, it was a simple matter of cleaning that thing up so my eyes didn't bleed and connecting the Android side with the React Native one.

My next concern was the SQLite database, which I haven't used with React Native but only with Android, and after some investigation I found [react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage) which did the job, didn't require hundreds of dependencies and didn't get on my way. More on that on part II.

Another concern was with the handling of currencies and dates, but come on, it's 2017 and we have Moment and [Big](https://github.com/MikeMcl/big.js) which work flawlessly.

This are all the dependencies used:

* react-native 0.47.2
* Big.js for decimal number handling.
* Moment for date handling.
* react-navigation (I love this one)
* react-native-vector-icons and Material Icons (don't start a project without this one)
* react-native-sqlite-storage
* react-native-fs

### Why Not Android Native

This came to my mind, but after you've done some React Native apps you'll understand that the UI and business logic is so much easier to implement on JavaScript than on Java. Also, the development flow is much faster, since pressing `rr` on the emulator refreshes all your changes without having to wait for any build process.

Specially the UI is so much easier to implement thanks to flex. Yes, there are some limitations, like the ones with `TextInput` but those weren't a big concern for this project.

![Main POS Screen](https://thepracticaldev.s3.amazonaws.com/i/nt722f97i1f7ennpxn29.png)

## Bluetooth Printer

This how my Android source folder ended up like:


```
.
â””â”€â”€ com
    â””â”€â”€ polinekopos
        â”œâ”€â”€ bt
        â”‚Â Â  â”œâ”€â”€ BTModule.java
        â”‚Â Â  â”œâ”€â”€ BTModulePackage.java
        â”‚Â Â  â””â”€â”€ sdk
        â”‚Â Â      â”œâ”€â”€ BluetoothService.java
        â”‚Â Â      â”œâ”€â”€ Command.java
        â”‚Â Â      â”œâ”€â”€ PrinterCommand.java
        â”‚Â Â      â””â”€â”€ PrintPicture.java
        â”œâ”€â”€ MainActivity.java
        â””â”€â”€ MainApplication.java
```

Under the `sdk` package is everything the printer came with so it was only up for me to create the React Native interface with it. There's also a `btsdk.jar` file with other dependencies on the `libs` folder.

`BluetoothService` is where all the magic happens and after going through the code and doing some refactoring (like adding a listening thread which would notify of disconnection events) I got a good grasp of how the communication with the Bluetooth printer magic worked. TL:DR it involves a lot of bytes and buffers.


`BTModule` manages everything between this service and the React Native app:

```java
public class BTModule extends ReactContextBaseJavaModule {
    public BTModule(ReactApplicationContext reactContext) {
        ...
    }

    @ReactMethod
    public void connect(String address, Promise promise) { ... }

    @ReactMethod
    public void disconnect(Promise promise) { ... }

    @ReactMethod
    public void print(String message, Promise promise) { ... }

    @ReactMethod
    public void getState(Promise promise) { ... }

    private void sendEvent(String eventName) { ... }
}
```

These methods are quite straight forward and are available for React Native to use. Everything else is as explained on [this section of the React Native documentation](https://facebook.github.io/react-native/docs/native-modules-android.html).

There's also some modifications on `MainActivity` to suspend and resume the connection with the printer:

```java
@Override
public synchronized void onResume() {
    super.onResume();
    if (mBluetoothAdapter == null) {
        return;
    }

    if (BluetoothService.get().getState() == BluetoothService.STATE_NONE) {
        BluetoothService.get().start();
    }
}
```

On the React Native side it was very simple. A button to connect/disconnect on the top bar which also shows the status by listening to Bluetooth events and sending strings to the printer.

```javascript
export class Banner extends React.Component {
    state = {
        bluetooth: 'bt-unknown'
    };
    
    componentWillMount() {
        events.forEach((e) => DeviceEventEmitter.addListener(e, () => this.setState({bluetooth: e})));
    }
    
    componentWillUnmount() {
        events.forEach((e) => DeviceEventEmitter.removeAllListeners());
    }

    getBluetoothIcon() {
        let icon = null;
        switch (this.state.bluetooth) {
            case 'bt-unknown':    icon = IconFactory.Icons.BTUnknown; break;
            case 'bt-connecting': icon = IconFactory.Icons.BTConnecting; break;
            case 'bt-connected':  icon = IconFactory.Icons.BTConnected; break;
            case 'bt-disconnect': icon = IconFactory.Icons.BTDisconnected; break;
        }
        return IconFactory.build(icon, styles.drawerIcon);
    }

    async onBluetooth() {
        const bluetooth = await BTModule.getState();
        this.setState({bluetooth: bluetooth});

        if(bluetooth !== 'bt-connected'){
            const id = ConfigManager.of().getBluetoothID();
            try {
                const deviceName = await BTModule.connect(id);
                AlertFactory.info(Messages.get('message.bluetooth.connected'));
            } catch (e) {
                AlertFactory.error(Messages.get('error.bluetooth.connection'));
            }
        }
    }

    render() {
        return (
            // ...
            <View style={styles.bannerRight}>
                <TouchableHighlight
                    underlayColor='transparent'
                    style={styles.drawerButton}
                    onPress={this.onBluetooth.bind(this)}>
                        { this.getBluetoothIcon() }
                </TouchableHighlight>
            </View>
        );
    }
}
```

Here `DeviceEventEmitter` manages the events sent from the native side of the app from the `sendEvent` method defined on `BTModule` and renders the view with the state it received. This way the Bluetooth icon is always up to date.

The `onBluetooth` function is binded to the button and checks if we're disconnected and tries to connect again. Whatever happens, an alert is shown to the user.

The `IconFactory` object takes care of managing and rendering the icons provided by [react-native-vector-icons](https://github.com/oblador/react-native-vector-icons).

Printing is rather easy, although it requires to use a lot of `padStart` and `padEnd` to align the content as desired. Anyway, after an array with all the lines is created, a call to the `write` function is all the is required:

```javascript
    async print() {
        // Some margin
        await BTModule.print(' ');
        await BTModule.print(' ');

        // Now the ticket
        this.lines.forEach((line) => BTModule.print(line));

        // Cut margin
        await BTModule.print(' ');
        await BTModule.print(' ');
    }
```

![This is how a ticket looks like after it was printed](https://thepracticaldev.s3.amazonaws.com/i/s22156kulmfaboz3lzk7.png)

This is it on part I, hope you enjoyed and look forward to part II where I'll discuss the SQLite side of things and some nice to know things about react-navigation.

BTW, did you catch that bug on the last picture? Looks like more work!

### Footnotes

1. When buying this sort of stuff on Amazon, be careful with the "Buy them together" section. Big mistake on my side. The printer we bought didn't have any port to connect the drawer (it's a small telephone like port) so we have to manually open it and the thermal paper were the wrong ones too.