left-icon

Writing Native Mobile Apps in a Functional Language Succinctly®
by Vassili Kaplan

Previous
Chapter

of
A
A
A

CHAPTER 1

Introduction

Introduction


“Code is like humor. When you have to explain it, it is bad.”

Cory House

What will be covered in this book?

In this book, we are going to see how to develop native mobile apps in a customizable scripting language. In my previous e-book, Implementing a Custom Language Succinctly, I showed how to create this customizable language. For brevity, I called this language CSCS (Customizable Scripting in C#). My previous e-book is not a prerequisite for this book, although it is a “nice-to-have,” since it has a few details about the CSCS language that we will not cover in this book.

Here’s what we’ll cover:

  • How to write native mobile apps for iOS and Android in a scripting language.
  • How to place widgets on the screen programmatically (no storyboards, XML, or XAML needed—it’s all in a simple scripting language).
  • How to add custom widgets to CSCS.
  • How to implement basic functionality in CSCS: text-to-speech and voice recognition, in-app purchases, scheduling events, etc.
  • How to add other Xamarin Frameworks and NuGet packages that can be used in CSCS.
  • How to do Unit Testing in CSCS.
  • How to call native C# code from CSCS and get the results back.
  • Examples of complete apps written in pure CSCS.

The base developing platform will be Microsoft Visual Studio (either on Windows or on macOS) with Xamarin. I personally use the free one, Visual Studio Community 2017 on macOS.

The CSCS language does not have to be used exclusively for developing a mobile app. It can also be used as a complement to facilitate creating common widgets, their placements, and any other functionality—CSCS is 100 percent customizable.

You can also write the whole app in CSCS—I did it with the iLanguage app. The same CSCS code is used for both iOS and Android. The code for iLanguage app is in the source code download—the entry point is the iLanguage.cscs file, which imports a few other CSCS files.

I also wrote an app to test CSCS scripts on iOS and on Android. You can test all of the scripts that you see in this book and also experiment with your own. The app itself is obviously written in CSCS as well. The source code is in the cscs.cscs file.

Advantages of using CSCS for mobile

Why should you use a scripting language to write an app?

  • There is much less coding. As you will see in this book, it takes only a very few lines of code to place a widget on the screen and populate it with some data and images.
  • The same code is used for both iOS and Android. You will also see that other platforms, like Windows Phone, can be easily added to CSCS as well.
  • Even though Xamarin.Forms can share up to 75 percent of common code between iOS and Android, or even more if only common controls are used, CSCS goes further than that because it is easier to add features shared by multiple platforms. Once you have C# wrappers over the native code, you use same CSCS code to call them behind the scenes for all platforms.
  • Quicker debugging: no need to recompile the code when debugging the app, since the changes are done in the scripting part, not in C#.
  • The CSCS parser is not only open source, but also included with every project, so it is easy to modify the existing functionality or add a new one on the fly—compare this with C# or Python.

“Hello, World!” in CSCS

Code Listing 1 contains CSCS code to create a few labels, a button, and an event triggered when the user clicks on a button.

Code Listing 1: “Hello, World!” in CSCS

AutoScale();
SetBackgroundColor("cyan");

locClickme = GetLocation("ROOT""CENTER""ROOT""CENTER");
AddButton(locClickme, "buttonClickme""Click me"20080);
AddAction(buttonClickme, "clickme_click");
SetFontSize(buttonClickme, 12);

locVersionLabel = GetLocation(buttonClickme, "ALIGN_LEFT",

                            buttonClickme, "TOP");
AddLabel(locVersionLabel, "versionLabel"""36060);
SetFontSize(versionLabel, 12);

locSizeLabel = GetLocation(buttonClickme, "CENTER",

                         buttonClickme, "BOTTOM");
AddLabel(locSizeLabel, "sizeLabel"""36060);
SetFontSize(sizeLabel, 10);

locOrientationLabel = GetLocation(buttonClickme, "RIGHT",

                               buttonClickme, "CENTER");
AddLabel(locOrientationLabel, "orientationLabel"""16080);
SetFontSize(orientationLabel, 10);

clicks = 0;
function clickme_click(sender, arg)
{
  clicks++;
  if (clicks == 1) {
    SetText(versionLabel, "Hello, " + _DEVICE_INFO_ + ", " +
                          _VERSION_INFO_, "left");
  } elif (clicks == 2) {
    SetText(sizeLabel, "Size: " + DisplayWidth + "x" + DisplayHeight +
                       ". Locale: " + GetDeviceLocale(), "center");
  } elif (clicks == 3) {
    SetText(orientationLabel, "Orientation: " + Orientation, "center");
  } else {
    SetText(buttonClickme, "Clicks: " + clicks, "center");
  }
}

See the results of running the “Hello, World!” script on actual devices in Figure 1. The image on the left shows it running on an iPhone 6, and the one on the right shows it running on a Wiko Lenny4 Android phone.

Running the "Hello, World!" Script for iPhone (left) and Android (right)

Figure 1: Running the "Hello, World!" Script for iPhone (left) and Android (right)

Let’s analyze Code Listing 1. AutoScale()scales the widgets according to the screen resolution.

Tip: Using the AutoScale() function, the width of a widget will be twice as large on a device with a width of 960 pixels, than on a device with a width of 480 pixels.

You can also use the auto-scale functionality per widget. We will see more details about this function in the next chapter.

SetBackgroundColor("cyan") will change the background color on the running device. There is also a SetBackground(imageName) function, which sets an image as a background.

The next two lines create a location and a button, which will be placed in that location:

locClickme = GetLocation("ROOT""CENTER""ROOT""CENTER");
AddButton(locClickme, "buttonClickme""Click me"20080);

The general signature for creating a location on the screen is the following:

GetLocation(HorizontalReference, PlacementX,

            VerticalReference,   PlacementY);

Horizontal or vertical references are just some other widgets on the screen, or a ROOT, meaning the main screen.

To connect an action to the event of clicking on a button, the following function is used:


AddAction(buttonClickme, "clickme_click");

The callback function, in our case clickme_click, always has two parameters, a sender and an argument. As you can see, this CSCS concept has been borrowed from C#, as have a few others.

The clickme_click() function has a few calls to other functions, namely to: _DEVICE_INFO_, _VERSION_INFO_, DisplayWidth, DisplayHeight, GetDeviceLocale, and Orientation.

All of these functions are implemented in C#. None of them require a parameter—in this case, the opening and closing parentheses after the function name are optional. We will talk about the implementation of some of these functions later on.

Customizable scripting in C#

Syntax-wise, CSCS is a mixture of Python and C#. But CSCS is not object-oriented, even though it has some features of object-oriented languages. One of them is polymorphism: there are many CSCS functions that accept different widgets as arguments, like SetFontColor(), SetValue(), SetText(), and many others. We will talk about these functions later on.

The CSCS language is implemented in C# and it is based on the split-and-merge algorithm. Let’s briefly review the split-and-merge algorithm, which forms a basis of CSCS. You can check out the complete description here.

The algorithm consists of two steps. In the first step, the string containing an expression is split into a list of so-called “variables.” Each variable consists of a number or a string and an action that must be applied to it. The numbers are internally represented as doubles, so in essence, they can be integers or Booleans.

Actions are different for numbers and strings. For numbers, the actions can be all possible operations we can do with the numbers, for example, +, , *, / or %, Boolean operators (!, &&, ||), and bitwise operations (&, |, ^). For strings, the actions can be a + (string concatenation) or Boolean comparisons. For convenience, we denote the action of the last variable in the list as ).

As soon as we get any function or an opening parentheses in the expression string, we recursively apply the whole split-and-merge algorithm to the expression in parentheses or to a function, and then replace them with the calculated result.

At the end of the first step, we are going to have a list of variables, each one consisting of either a number or a string, and an action that will be applied.

In the second step, we merge the list of variables created in the first step. This list is merged one by one, starting from the first variable in the list. Merging means applying the action of the left variable to the values of the left and right variables. The resulting variable will have the same action as the right variable.

The variables can only be merged if the priority of the action of the left variable is greater than or equal to the priority of the action of the right variable (for instance, the priority of multiplication is greater than the priority of addition). If two variables cannot be merged, we temporarily move to the next-right variable, in order to try to merge it with the variable next to it, and so on, recursively. As soon as the right variable has been merged with the variable on its right, we return to the original, left variable, and try to re-merge it with the newly created right variable.

Eventually, we will be able to merge the whole list into one variable, since sooner or later we will reach the last variable of the list that has the lowest priority, and therefore, can be merged with any variable on its left. The value of the remaining variable will be the final result.

An example of implementing a CSCS function

Let’s see an example of the implementation of the _DEVICE_INFO_ function that you saw in Code Listing 1. The expected outcome of this function is the name of the device being used (for example, iPhone 6, or Wiko Lenny4).

There are just two steps in adding a new function to CSCS:

  1. Implement the Evaluate() method of a class deriving from the ParserFunction class.
  2. Register the class implemented in the previous step with the parser.

The implementation is different for iOS and Android. Code Listing 2 has an Android implementation. The iOS implementation is unfortunately, and surprisingly, much more complicated, at least as of iOS 11.2, and will be skipped (the curious reader can check it out in the accompanying source code, which can be downloaded).

Code Listing 2: Implementation of the GetDeviceInfoFunction Class for Android

class GetDeviceInfoFunctionParserFunction
{
  protected override Variable Evaluate(ParsingScript script)
  {
    string deviceName = Android.OS.Build.Brand;
    string model      = Android.OS.Build.Model;
    if (!model.Contains("Android")) {
      // Simulators have "Android" in both, Brand and Model.
      deviceName += " " + model;
    }

    return new Variable(deviceName);
  }
}

Once a class, deriving from the ParserFunction class, is implemented, we can register it with the parser as follows:

ParserFunction.RegisterFunction("_DEVICE_INFO_",

                                 new GetDeviceInfoFunction());

The general signature is:

ParserFunction.RegisterFunction(functionName, aParserFunction);

This way you can register any function with the parser. We are going to see many more examples of this later on in this book.

Summary

In this chapter we saw the split-and-merge algorithm and how it is used to implement a typical CSCS function. This algorithm is used behind the scenes in all CSCS functions, but you do not have to know it to use it directly—it’s all done behind the scenes by the CSCS parser.

In the next chapter, we are going to see the overall Xamarin project structure that can be used for customized scripting with CSCS.

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.