Say Numbers Part 2: Project Setup and Services

In this part we are going to start creating the project from scratch.  These instructions are for Xamarin Studio using OS X, but there should be a similar way to do it in Visual Studio if you so wish.  Create a new “Blank Xamarin.Forms App” solution, and give it an identifier of your choosing and a title of SayNumbers.  You will now have four projects in your solution: SayNumbers.iOS, SayNumbers.Droid, SayNumbers.Shared and SayNumbers.UITests.  Let’s set up each one of these projects.

Under the SayNumbers project, delete SayNumbers.cs.  As of this blog post, the nuget packaging for MvvmCross does not work for Xamarin Studio since it requires Nuget 3, but as a workaround paste this as your packages.config file:


<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MvvmCross.HotTuna.Binding" version="4.0.0-alpha9" targetFramework="portable-net45+win+wp80+MonoTouch10+MonoAndroid10+xamarinmac20+xamarinios10" />
<package id="MvvmCross.HotTuna.CrossCore" version="4.0.0-alpha9" targetFramework="portable-net45+win+wp80+MonoTouch10+MonoAndroid10+xamarinmac20+xamarinios10" />
<package id="Xamarin.Forms" version="1.4.4.6392" targetFramework="portable-net45+win+wp80+MonoTouch10+MonoAndroid10+xamarinmac20+xamarinios10" />
</packages>

This will add the correct frameworks to your shared project.  After that right click your “packages” folder and select “Restore”.

For the Android project, Delete MainActivity.cs and use this for packages.config:


<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Cirrious.MvvmCross.Forms.Presenter" version="3.5.2-alpha2" targetFramework="MonoAndroid50" />
<package id="MvvmCross.HotTuna.Binding" version="4.0.0-alpha9" targetFramework="MonoAndroid50" />
<package id="MvvmCross.HotTuna.CrossCore" version="4.0.0-alpha9" targetFramework="MonoAndroid50" />
<package id="MvvmCross.HotTuna.MvvmCrossLibraries" version="4.0.0-alpha9" targetFramework="MonoAndroid50" />
<package id="Xamarin.Android.Support.v4" version="22.2.1.0" targetFramework="MonoAndroid50" />
<package id="Xamarin.Forms" version="1.4.4.6392" targetFramework="MonoAndroid50" />
</packages>

And for the iOS version, delete AppDelegate.cs and use this for packages.config:


<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Cirrious.MvvmCross.Forms.Presenter" version="3.5.2-alpha2" targetFramework="xamarinios10" />
<package id="MvvmCross.HotTuna.CrossCore" version="3.5.2-alpha2" targetFramework="xamarinios10" />
<package id="MvvmCross.HotTuna.MvvmCrossLibraries" version="3.5.1" targetFramework="xamarinios10" />
<package id="MvvmCross.PortableSupport" version="3.5.2-alpha2" targetFramework="xamarinios10" />
<package id="Xamarin.Forms" version="1.4.4.6392" targetFramework="xamarinios10" />
</packages>

Now onto the actual services.  We are going to create a service for our application that will format an integer for printing to the screen. Create a folder called Services in your project, and add two files to it: INumberConversionService.cs and NumberConversionService.cs.

INumberConversionService


This is the interface, and we are going to add three methods to it:

string ToEnglish(int intValue);
string ToJapanese(int intValue);
string ToFormattedNumber(int intValue);

The first method is responsible for printing the number in words (e.g. 123 -> One Hundred Twenty Three).  The second will print Japanese text (e.g. 54321 -> 5万4321), and the final method will print the number with proper comma placement for American style numbers (e.g. 654321 -> 654,321).

NumberConversionService


This is the concrete implementation of the INumberConversionService interface.  Since it doesn’t require any platform specific logic we can go ahead and implement it straight away.  You might think that creating an interface like this is pointless, but this allows easy swapping of services in case you want to use different implementations for unit testing, for example.  Let’s start with transforming an integer to English words.  Well it just so happens that there is a repeating pattern in English spoken numbers and it goes like this:  [Three Digit Number] [Identifier] [Three Digit Number] [Identifier], etc.  Identifier is simply something like “Thousand” or “Million.”  As luck would have it, all [Three Digit Number] items follow the same pattern as well:  [Digit] Hundred ([Tens][Digit] | [SpecialTeen]).  [Digit] is simply a number “One” through “Nine,” [Tens] is “Twenty” through “Ninety” and [SpecialTeen] is “Ten” through “Nineteen.”  Our algorithm will start from the ones place and go through the highest place, whatever it may be.  This is the source code:

First we have some constants:

private static readonly string[] NUMBER_STRINGS = { String.Empty, "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen" };

private static readonly string[] TENS_STRINGS = { String.Empty, String.Empty, "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety" };

private static readonly string[] PLACES_STRINGS = { String.Empty, "Thousand", "Million", "Billion" };

Then we have our implementation:

var sb = new StringBuilder();
int val = intValue;
int place = 0;
while(val > 0) {
    sb.Insert(0, " ");
    sb.Insert(0, PLACES_STRINGS[place++]); // [Identifier]
    var remainder = val % 1000; // [Three Digit Number]
    var hundredsPlace = remainder / 100;
    var tensPlace = (remainder / 10) % 10;
    var onesPlace = remainder % 10;

    sb.Insert(0, " ");
    if(tensPlace == 1) { 
        sb.Insert(0, NUMBER_STRINGS[10 + onesPlace]); // [SpecialTeen]
    } else { 
        sb.Insert(0, NUMBER_STRINGS[onesPlace]); // [Digit]
        if(!String.IsNullOrEmpty(NUMBER_STRINGS[onesPlace])) {
            sb.Insert(0, " ");
         }
        sb.Insert(0, TENS_STRINGS[tensPlace]); // [Tens]
    }

    if(hundredsPlace > 0) {
        sb.Insert(0, " Hundred ");
        sb.Insert(0, NUMBER_STRINGS[hundredsPlace]); // [Digit]
    }

    val /= 1000;
}

return sb.ToString().Trim();

Compared to this method, the other two methods are relatively simple. Let’s implement ToJapanese.

First we need some additional identifiers.  万 is Japanese for “10 thousand” and 億 is Japanese for “100 million.”

private static readonly string[] JP_PLACES_STRINGS = { String.Empty, "万", "億" };

Now we can implement the algorithm, it is just passing 4 digits of the number followed by an identifier:

var sb = new StringBuilder();
int val = intValue;
int place = 0;
while(val > 0) {
    sb.Insert(0, JP_PLACES_STRINGS[place++]);
    var remainder = val % 10000;
    sb.Insert(0, remainder);

    val /= 10000;
}

return sb.ToString();

The final method is very simple. It just needs to print out three digits at a time separated by commas:

var sb = new StringBuilder(intValue.ToString());
for(int i = sb.Length - 3; i > 0; i -= 3) {
    sb.Insert(i, ',');
}

return sb.ToString();

Now our service is implemented! Excellent. That is all for this section. Next up we will be handling things at the view model level!

Advertisements

One thought on “Say Numbers Part 2: Project Setup and Services

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s