An sample inventory app demonstrating safe counters via Lists and Realm Counters
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
Graphics
InventoryApp
.gitignore
CONTRIBUTING.md
LICENSE
README.md

README.md

Inventory


A Realm Mobile Platform Example Application


Intro

This app is a simple implementation of an idealized inventory tracking system designed to show how transaction safe counters can be implemented in a mobile application.

Installation

Prerequisites

This app uses Cocoapods to set up the project's 3rd party dependencies. Installation can be directly (from instructions at the Cocapods site) or alternatively through a package management system like Homebrew.

Realm Mobile Platform

This application demonstrates features of the Realm Mobile Platform and needs to have a working instance of the Realm Object Server available to make tasks, and other data available between instances of the Inventory app. The Realm Mobile Platform can be downloaded from Realm Mobile Platform and exists in two forms, a ready-to-run macOS version of the server, and a Linux version that runs on RHEL/CentOS versions 6/7 and Ubuntu as well as several Amazon AMIs and Digital Ocean Droplets. The macOS version can be run with Inventory right out of the box; the Linux version will require access to a Linux server.

3rd Party Modules

The following modules will be installed as part of the Cocoapods setup:

  • RealmSwift The Realm bindings for Cocoa/Swift

  • Realm LoginKit A Realm control for logging in to Realm servers

  • BarcodeScanner an elegant barcode scanner module for iOS

  • Eureka a formbuilder for iOS in Swift by xmartlabs

  • ImagePicker for selection of photo library images by Hyper.no

  • ImageRow an image row component for the Eureka formbuilder

  • PermissionScope permission management dialog by Nick O'Neill

Preparing the Realm Object Server

The Inventory application can be used with any version of the Realm Object Server (Developer, Professional or Enterprise). The Inventory app needs to be able to set permissions on the Realm used by the app - this must be done by a Realm user that has permission/rights to administer the server. This could be the first account you set up as part of the Realm Object Server installation, or any account that has the admin bit set.

You can determine which accounts have admin rights by logging in to the Realm Object Server Dashboard: ROS Dashboard User Listing

You create new users (and give them admin rights) on this screen.

You can set the admin rights for existing users by clicking-through to the user's profile page and checking the "can administer this server" checkbox: ROS Dashboard User Listing

Once you have created or selected an admin user to use, you can proceed with compiling and running Inventory.

Compiling & Running the Application

Before attempting to compile the project, install all of its dependencies using Cocoapods by invoking pod install. This is done by opening a Terminal window and changing to the directory where you downloaded the Inventory repository. In this main directory is a Folder called InventoryApp that contains Podfile needed by CocoaPods as well as the application sources.

This process will create a Pods directory which contains all of the compiled resources needed by the app, along with an Xcode xcworkspace file which you will open and work with instead of the Inventory.xcproject file when building/running this application.

Once the cocoapods have been retrieved, open the Inventory.xcworkspace file and press build. The app should compile cleanly.

First Login

As mentioned above, the first login to the Inventory app needs to be by a user enabled with administrative privileges on the Realm Object Server. This is to enable a global Read/Write permission on the shared Realm that is created by the application.

Adding Users

Adding users can be done either via the Realm Dashboard, or by adding users using the Inventory the app itself from the login screen.


Navigating Inventory

The Inventory app is a simple "tab bar" application - that is to say it supports a number of main views that are accessible at all times:


Tab Bar
In this case, Inventory is very simple and supports just the main products list (in essence the "inventory" managed by the app) and a settings screen on which you can manage your profile or log out of the app.

The app does support several other detail screens that are explained along with the main applications screens these are:

  • Product view
  • Settings view
  • Barcode scanning view for finding existing or creating new products)
  • Product Detail view for seeing the details of existing products
  • Product entry view for entering the details of a newly created product

App Permissions

Inventory shows how do use some specialized features of Realm and is not meant to be a full-blown inventory system; never the less is can be used my multiple users as a way to try out the safe counters and sync'd transactions demonstrated here. In order to enable multiple users Inventory implements a top-level Realm named "Inventory" that has its permissions set to "* RW" which in Realm's permission structure is a wildcard read/write permission.

In order to set up the app fo multi-user access, the first user to log in using this app needs to be a Realm admin user -- that is usually the user ID of the person who set up the Realm object server; it can also be any other user who has been granted admin permissions via the Realm dashboard. See Preparing the Realm Object Server, above.

The Products View


Product Listing

Products can be viewed across a number of dimensions - tapping the sort menu in the upper-left corner of the products view will bring up the sort options menu, and tapping the arrow immediately to the right will change the sorting direction


The Barcode Scanner & Product Detail Entry

In order to use the barcode scanner built in to Inventory, you must give permission for the Inventory app to access the iOS device's camera:

Camera Permission Request

However, if you are running this application in the OS simulator under Xcode, there is no camera to access. You will need to enter product UPC codes by hand when using the simulator:

Simulator Warning

New Product Creation

A new product can be entered into Inventory in 2 ways:

  • tapping the "+" button on the product listing screen. This will bring up an empty form that can be filled in


  • Scanning a barcode - if the (UPC) scan code is not currently represented in the products:

Barcode Scanning

If the product is not yet in the system you are given the option to add it:

Scancode Not Found

Adding a product image to your inventory record:


If a product you scanned is already in the inventory system, it will be displayed:

Successful Scan

Other Ways to Search

The main products view also supports a search bar that will allow search to be done for any of the product item fields.

Adding/Removing Inventory

You can add to or remove items from inventory for given product on it's detail screen:


The counter and button will indicate the number to be added or subtracted, pressing the button will write the transaction to the Realm and update all the counters visible in the app live and in real-time (and if your app is online in the views other other users as well).


Settings / Profile


Application Architecture

Tracking Additions and Subtractions From Inventory

One of the interesting aspects of a mobile application is the fact it -- to be truly useful -- needs to work both online and offline. Our inventory application as an additional constraint: it needs to support changing quantities inside the application in a consistent and mathematically correct way regardless of how many users are accessing or updating the items.

While this may seem self-evident, supporting arithmetical atomicity is tricky. Realm supports 2 different ways of ensuring that transactions (additions or subtractions) from inventories: Lists and Counters.

Method 1: Lists

TBD: show how adding up the transactions in a list will yield a consistent result w/out the use of atomic counters.

Let's look at a transaction log for a few hypothetical products:


Here we see there several products - (product ID's 1,2 3 and 5) each with several additions or removals (sales) from the inventory.

If we highlight just the activity for product 1, we can see quickly where this goes - adding up the amounts of all the transactions tells use the current quantity on-hand of product #1. This is a very safe way to implement a transaction safe counting system. It also has the ability to allow us to create a rich system where we can add more color (metadata) to our inventory system (like the ID of who sold what, what store or region did gets credit for a sale, etc).


Which comes down to: 100+(-8)+(-20)+10+(-50)+(-15) or a total of 17 on hand.

Method 2: Counters

Realm also supports access safe counters - This method will be described in the next update to this demo.

Models

The Inventory app consists of two basic models - a Product:

class Product : Object {
    dynamic var id = ""                 // this should be something that's universal, like a UPC
    dynamic var creationDate: Date?
    dynamic var lastUpdated: Date?
    dynamic var productName = ""
    dynamic var productDescription = ""
    dynamic var image : Data? // binary image data, stored as a PNG
    var amount: Int {
        get {
            return self.quantityOnHand()
        }
    }
    let transactions = List<Transaction>()
    // :
    // more class methods
    // :
} // of Product

...and a Transaction:

class Transaction : Object {
    dynamic var id = NSUUID().uuidString    // every transaction is unique but the person doing it, the products and amounts, of course are not
    dynamic var transactionDate: Date?
    dynamic var transactedBy = ""           // a Realm SyncUser.identity
    dynamic var productId = ""              // the product this refers to
    dynamic var amount = 0                  // positive = inventory addition, negative == sale or inventory reduction

    // Initializers, accessors & cet.
    override static func primaryKey() -> String? {
        return "id"
    }
}

The Product class is a minimalistic, idealized implementation of what a product in an inventory system might look like. The transaction class tracks all the required info about an addition to or sale from the inventory for any product. The key things to note in the Product class are:

  1. We use a Realm list property to be able to find all of the transactions (sales, and inventory replenishments) that are booked against this product, and

  2. The "amount" property is not actually stored in the Realm itself but is a synthesized property whose getter uses a class method to get the current quantity on hand for a given product.

Interestingly there a number of ways you could do this. For example, you could get a list of all of the transactions registered against a product, use Swift's map function to extract the transaction amounts and then sum the up using reduce:

func quantityOnHandUsingMapReduce() -> Int {
    let realm = try! Realm()
    let transactions = realm.objects(Transaction.self).filter("productId = %@", self.id).map{$0.amount} // get all the amounts for the product as an array
    return transactions.reduce(0, +) // now sum them to get the QoH
}

This is a perfectly valid way to do these kinds of aggregated calculations, but a better way uses Realm's built-in aggregation functions to do math for us:

func quantityOnHand() -> Int {
    let realm = try! Realm()
    let transactions = realm.objects(Transaction.self).filter("productId = %@", self.id)
    return transactions.sum(ofProperty: "amount")
}

The result of the use of the transaction list and the synthesis of the amount on hand using Realm's aggregation functions enables multiple users to use an app like Inventory and not worry about collisions or math errors and is very efficient.

For this small demo app we've implemented just add/subtract from the product count, but of course one can add any number of such multi-user safe operations. The implementation file here includes a quantitySold() function and the suggested outlines for functions to determine quantities sold across date ranges as well.

Contributing

See CONTRIBUTING.md for more details!

This project adheres to the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to info@realm.io.

License

The Realm Inventory is distributed under the terms of the Apache License analytics