Bineroo 1.3: one Day, one Grid

dargil
7 min readJan 30, 2021

--

Bineroo v1.3 is now available in the app stores:

iOS: https://apps.apple.com/us/app/id1457498721

Android: https://play.google.com/store/apps/details?id=com.dargil.bineroo

What’s new in v1.3?

With this release, I introduce the concept of the daily challenge.

Every day, a new grid (of any size and difficulty) is randomly selected and proposed as the grid of the day to all players.

Anyone can play it (without spending any of his coins) and get the reward once the grid is completed (like any other grid).

The ultimate goal of this feature is to build a daily world competition with ranking and additional rewards for the best ones. I will work on that as soon as I reach at least 100 daily users (FYI: currently I am just over 10).

You know what to do if you want to see this happening …. Share, share, share ;)

How does it work in detail?

This section is more for technical guys so I won’t be offended if you don’t read it :D

Once again, I am not going to give all the details but only the ones I believe are more relevant. Feel free to reach out to discuss further.

This release was the chance to work with a bunch of Firebase tools:

  • Cloud Firestore
  • Cloud Functions
  • Cloud Messaging

Setting up Firebase is out of the scope of this article. If you have any trouble with that, do not hesitate to comment.

First, I implemented the mechanism to request, from the device, the daily grid information.

Workflow “Getting the daily grid”

The request from the device is a simple API call that triggers a cloud function.

Writing, testing and deploying a cloud function on Firebase is pretty straightforward. Just follow the official documentation: https://firebase.google.com/docs/functions/get-started

I decided to store daily grid information in Cloud Firestore, which is a No-SQL database (similar to MongoDB).

The cloud function is just getting the latest element inserted in the collection and returns it as part of the HTTP response.

A general idea of how it works here:

Few comments:

  • functions.region() gives you the possibility to change the region where your cloud function will be hosted. By default it is your project default region.
  • https.onRequest clearly shows that the cloud function is triggered on a HTTPs request. This is very convenient as you can test it directly from your browser. WARNING: don’t forget to protect access with IAM if you are exposing sensitive information (not the case here)
  • I found out that the JSON response must return information inside a data object. I am not sure it is absolutely mandatory. But as far as I remember it was not working well on the device side without it. To be clarified.

Once the cloud function is deployed, we can work on the device side.

As this is similar to an API call, requesting the information is done in an async function. Here is a simple version of it (in a production-ready method, all error cases must be handled).

How I modified the layout is not part of this article as I want to focus on the new stuff (i.e. Firebase integration).

Now we have a cloud function that exposes the daily grid information and everything ready on the device side to fetch this information.

The initial requirement is to automatically select a new grid every day. Grids that have already been selected cannot be selected again.

From a database point-of-view, this is just adding a new entry to the daily collection. This operation is executed every day at the same time.

Firebase Cloud function offers a great solution for that. You can build and deploy scheduled cloud function (official documentation here: https://firebase.google.com/docs/functions/schedule-functions)

This is how it looks

Few comments:

  • pubsub.schedule(‘0 8 * * *’) lets you define the periodicity of the function execution. Here it is defined as a classic Unix Crontab format but you can also use AppEngine syntax (ex: ‘every 5 minutes’). You can also note that scheduling a cloud function is making use of PubSub (real-time messaging platform of GCP). Indeed, once your function is deployed, you can see in the GCP console that a PubSub topic and subscription have been automatically created. In addition, a Cloud Scheduler job has also been automatically created. This job will run on the period cycle you set when building your scheduled cloud function. On every run, it will basically just send a message on the PubSub topic. The subscription will catch the message and trigger the cloud function.
  • timeZone(‘Europe/Paris’) indicates the time zone of the scheduler. In this case, the scheduler is running every day at 8 am Paris time.
  • Important to use Firestore datatypes to insert elements in the collection. Here the field generated_at is of type admin.firestore.Timestamp

The final part of this new feature is about Notification.

Every time a new daily grid is selected, a notification is sent to all users to make them aware of the new daily challenge.

Firebase offers a great tool to manage notification: Cloud Messaging

Once again, I wouldn’t recommend not enough you have a look at the official documentation (https://firebase.flutter.dev/docs/messaging/overview) if you intend to use it. It really helps, especially regarding initial setup (integration of APNs — Apple Push Notification Service — is impossible to guess without it for instance).

Once configured you can rapidly start playing with it.

From the firebase console, it is possible to send any type of notification. No need for a backend server in the first place. You can either target specific devices (for test purposes) or topics (like in a real production environment).

In order to target a specific device, you need to retrieve the device FCM token with a few lines of code.

Once you get the token, you can use it in the console. Here is how it should look like.

This is very convenient when you want to rapidly and safely test your implementation.

In a production environment, messages will normally not be sent to specific devices. Messages will rather be sent to a topic.

In order to receive messages sent to this topic, the device must subscribe to the topic.

The application can be in one of the following state when receiving a notification.

Application states (from official documentation)

How you handle these various scenarios is done via callbacks you register for each of them.

For instance, to handle notification while application is in the Foreground you register the onMessage callback:

Note for later: I wanted to pop up a dialog box to indicate to the user a new daily grid was available but only if the home screen was visible. Not the other screens, especially the play screen because I find it disturbing while playing. To fulfil this requirements, I had to implement an internal messaging system and a mechanism to detect which screen is visible at all time. This is worth an article.

When the application is terminated, tapping on the notification will open it again. At this time, we can check the daily grid status from the server. There is no need to register any type of callback.

The final case is to handle notification while the application is in the background. Pressing the notification will bring it back in the foreground. If you need to perform any action at this stage, the onMessageOpenedApp callback has to be registered.

For iOS device, it is mandatory to request the user permission to receive notifications. This is done with a very simple piece of code:

I have brought it all together in a initFirebase method that looks like this:

Everything is ready on the device side. The last thing to do is to automate the pushing of a firebase message to the correct topic every time a new daily grid is selected. This requires only few additional line of code in our scheduled cloud function.

Nothing worth commenting here.

And this is nearly it.

After few days, I received a warning, from Google, like the one below.

Indeed, securing your database is very important. And this is not always obvious how to do so in this cloud environment.

Fortunatelly for me, that was easier than expected.

My data is accessed only via Google cloud functions: one to get the daily grid, one scheduled to insert a new grid every day. Cloud functions run with an admin level. Therefore, I can block any other access level by adding the following lines in your Firestore rules.

service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false; //deny all access. Cloud functions run under administrative privileges
}
}
}

The getDailyGrid cloud function is still publicly accessible via HTTP but that does not really matter as it just returns the daily grid information.

The scheduled cloud function is executed only on PubSub message and is consequently not publicly accessible. No harm possible.

And now, we are really done

One of the goals of this new feature is to increase user engagement. So I am expecting an increase in user retention. We’ll see. Stay tuned and have Fun.

--

--