Nikola Brežnjak blog - Tackling software development with a dose of humor
  • Home
  • Daily Thoughts
  • Ionic
  • Stack Overflow
  • Books
  • About me
Home
Daily Thoughts
Ionic
Stack Overflow
Books
About me
  • Home
  • Daily Thoughts
  • Ionic
  • Stack Overflow
  • Books
  • About me
Nikola Brežnjak blog - Tackling software development with a dose of humor
Programming

Code Complete 2 – Steve McConnell – Controlling Loops

I just love Steve McConnell’s classic book Code Complete 2, and I recommend it to everyone in the Software ‘world’ who’s willing to progress and sharpen his skills.

Other blog posts in this series:

  • Part 1 (Chapters 1 – 4): Laying the Foundation
  • Chapter 5: Design in Construction
  • Chapter 6: Working Classes
  • Chapter 7: High-Quality Routines
  • Chapter 8: Defensive programming
  • Chapter 9: Pseudocode Programming Process
  • Chapter 10: General Issues in Using Variables
  • Chapter 11: General Issues in Using Variables
  • Chapter 12: Fundemental Data Types
  • Chapter 13: Unusual Data Types
  • Chapter 15: Using Conditionals

“Loop” is a structure that causes a program to repeatedly execute a block of code. Common loop types are for, while, and do-while.

When to use the while loop

If you don’t know ahead of time exactly how many times you’ll want the loop to iterate, use the while loop. Contrary to what some novices think, the test for the loop exit is performed only once each time through the loop, and the main issue concerning while loop is deciding whether to test at the beginning or the end of the loop.

  • while – loop tests condition at the beginning
  • do-while – tests condition at the end, which means that the body of the loop gets executed at least once.
  • loop-with-exit – structure more closely models human thinking. This is the loop in which the exit condition appears in the middle of the loop rather than at the beginning or the end.

When to use the for loop

A for loop is a good choice when you need a loop that executes a specified number of times. If you have a condition under which execution has to jump out of a loop, use a while loop instead.

When to use the foreach loop

It is useful for performing an operation on each member of an array or some other container. It has an advantage of eliminating loop-housekeeping arithmetic and therefore eliminating any chance of errors in the loop-housekeeping arithmetic.

Controlling the loop

You can use two practices. First, minimize the number of factors that affect the loop. Simplify! Simplify! Simplify! Second, treat the inside of the loop as if it were a routine-keep as much control as possible outside the loop.

Entering the loop

  • Enter the loop from one location only
  • Put initialization code directly before the loop – Principle of Proximity advocates putting related statements together.
  • Use while ( true ) for infinite loops – that’s considered a standard way of writing an infinite loop in C++, Java, Visual Basic, and other languages. for(;;) is an accepted alternative.

Processing the middle of the loop

  • Use { and } to enclose the statements in a loop – Use brackets every time. They don’t cost anything in speed or space at runtime, they help readability, and they help prevent errors as the code is modified.
  • Avoid empty loops – In C++ and Java, it’s possible to create an empty loop, one in which the work the loop is doing is coded on the same line as the test that checks whether the work is finished. Here’s an example:
    while( (inputChar = dataFile.GetChar() ) != CharType_Eof ) {
        ;
    }
    

    The loop would be much clearer if it were recoded so that the work it does is evident to the reader:

    do {
        inputChar = dataFile.GetChar();
    } while ( inputChar != CharType_Eof );
    

The new code takes up three full lines rather than that of one line, which is appropriate since it does the work of three lines rather than that of one line.
+ Keep loop-housekeeping chores at either the beginning or the end of the loop – As a general rule, the variables you initialize before the loop are the variables you’ll manipulate in the housekeeping part of the loop.

Exiting the loop

  • Assure that the loop ends
  • Don’t monkey with the loop index of a for loop to make the loop terminate
  • Avoid code that depends on the loop index’s final value

Exiting loops early

The break statement causes a loop to terminate through the normal exit channel; the program resumes execution at the first statement following the loop.

continue causes the program to skip the loop body and continue executing at the beginning of the next iteration of the loop.

Inefficient programmers tend to experiment randomly until they find a combination that seems to work. If a loop isn’t working the way it’s supposed to; the inefficient programmer changes the < sign to a <= sign. If that fails, the inefficient programmer changes the loop index by adding or subtracting 1. Eventually, the programmer using this approach might stumble onto the right combination or simply replace the original error with a more subtle one. Even if this random process results in a correct program, it doesn’t result in the programmer’s knowing why the program is correct.

How long should a loop be?

Make your loops short enough to view all at once. Experts have suggested a loop-length limit of one page (about 50 lines of code). When you begin to appreciate the principle of writing simple code, however, you’ll rarely write loops longer than 15 or 20 lines.

Creating loops easily – from the inside out

If you sometimes have trouble coding a complex loop, which most programmers do, you can use a simple technique to get it right the first time.

Here’s the general process:
+ Start with one case.
+ Code that case with literals.
+ Then indent it, put a loop around it, and replace the literals with loop indexes or computed expressions.
+ Put another loop around that, if necessary, and replace more literals.
+ Continue the process as long as you have to
+ When you finish, add all the necessary initializations.

Since you start at the simple case and work outward to generalize it, you might think of this as coding from the inside out.

Android

How to make a native Android app that can block phone calls

TL;DR

In this post, I’ll show you step by step how to make a native Android app that can block certain numbers from calling you.

The source code is on Github.

I hope that my step by step guide that I’m going to show you here will help you and save you from doing additional research.

Of course, since I’m not a native Android developer in my day to day job, I’m doing it also for the fact that it will serve me as a good reminder for when I need to deal with a similar situation again. Shout out to the rest of you #jackOfAllTrades out there ?

Also, given the statement above; I would appreciate any feedback regarding this code. ?

!TL;DR

I’ve spent a lot of time going through StackOverflow and blog posts in search of this solution. Of all of those, these were helpful:

  • How to detect incoming calls on an Android device?
  • Can’t answer incoming call in android marshmallow 6.0
  • Android permission doesn’t work even if I have declared it
  • End incoming call programmatically
  • Is the phone ringing

But sadly, none of them was straightforward, beginner kind of tutorial. So, after a lot of additional research, I made it work, and here’s my best attempt at explaining how.

As a sidenote: while testing this, the discovery of how to simulate an incoming call or SMS to an emulator in Android Studio was also very helpful.

Starting a new project

In Android Studio go to File->New->New Project, give it a name and a location and click Next:

Leave the default option for minimum API level:

Select an Empty Activity template:

Leave the name of the activity as is:

AndroidManifest.xml

Set the permissions (two uses-permission tags) and the receiver tags in AndroidManifest.xml file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.nikola.callblockingtestdemo">

    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.CALL_PHONE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver  android:name=".IncomingCallReceiver" android:enabled="true" android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.PHONE_STATE" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

With the READ_PHONE_STATE permission we get this (as defined in official docs):

Allows read-only access to phone state, including the phone number of the device, current cellular network information, the status of any ongoing calls, and a list of any PhoneAccounts registered on the device.

With the CALL_PHONE permission we get this (as defined in official docs):

Allows an application to initiate a phone call without going through the Dialer user interface for the user to confirm the call.

⚠️ I found that even though not stated here, I need this permission so that I can end the call programmatically.

The receiver tag is used to define a class that will handle the broadcast action of android.intent.action.PHONE_STATE. Android OS will broadcast this action when, as the name implies, the state of the phone call changes (we get a call, decline a call, are on the call, etc.).

IncomingCallReceiver.java

Create a new class (File->New->Java Class), call it IncomingCallReceiver and paste this code in (note: your package name will be different than mine!):

package com.example.nikola.callblockingtestdemo;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
import android.widget.Toast;
import java.lang.reflect.Method;
import com.android.internal.telephony.ITelephony;

public class IncomingCallReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {

        ITelephony telephonyService;
        try {
            String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
            String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);

            if(state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_RINGING)){
                TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
                try {
                    Method m = tm.getClass().getDeclaredMethod("getITelephony");

                    m.setAccessible(true);
                    telephonyService = (ITelephony) m.invoke(tm);

                    if ((number != null)) {
                        telephonyService.endCall();
                        Toast.makeText(context, "Ending the call from: " + number, Toast.LENGTH_SHORT).show();
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }

                Toast.makeText(context, "Ring " + number, Toast.LENGTH_SHORT).show();

            }
            if(state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_OFFHOOK)){
                Toast.makeText(context, "Answered " + number, Toast.LENGTH_SHORT).show();
            }
            if(state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_IDLE)){
                Toast.makeText(context, "Idle "+ number, Toast.LENGTH_SHORT).show();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

In Android, if we want to ‘get’ the data from the BroadcastReceiver, we need to inherit the BroadcastReceiver class, and we need to override the onReceive method. In this method, we’re using the TelephonyManager to get the state of the call, and we’re using the ITelephony interface to end the call.

To be honest, this is where it gets a bit ‘weird’, as to get this ITelephony interface, you need to create the ITelephony interface.

ITelephony.java

To do that, create a new class (File->New->Java Class), call it ITelephony and paste this code in (note: overwrite everything with the below content; yes, even the weird package name):

package com.android.internal.telephony;

public interface ITelephony {
    boolean endCall();
    void answerRingingCall();
    void silenceRinger();
}

Android Studio will complain about package com.android.internal.telephony; (red squiggly dots under this package name), but that’s how it has to be set for this to work. I didn’t find the exact explanation why this has to be included, so if you know, please share it in the comments.

Requesting permissions at runtime

This was one thing that was hindering my success in getting this to work!

Namely, after Android 6.0+, even if you have permissions set in the AndroidManifest.xml file, you still have to explicitly ask the user for them if they fall under the category of dangerous permissions. This is the list of such permissions:

  • ACCESS_COARSE_LOCATION
  • ACCESS_FINE_LOCATION
  • ADD_VOICEMAIL
  • BODY_SENSORS
  • CALL_PHONE
  • CAMERA
  • GET_ACCOUNTS
  • PROCESS_OUTGOING_CALLS
  • READ_CALENDAR
  • READ_CALL_LOG
  • READ_CELL_BROADCASTS
  • READ_CONTACTS
  • READ_EXTERNAL_STORAGE
  • READ_PHONE_STATE
  • READ_SMS
  • RECEIVE_MMS
  • RECEIVE_SMS
  • RECEIVE_WAP_PUSH
  • RECORD_AUDIO
  • SEND_SMS
  • USE_SIP
  • WRITE_CALENDAR
  • WRITE_CALL_LOG
  • WRITE_CONTACTS
  • WRITE_EXTERNAL_STORAGE

To ask for such permissions here’s the code you can use (I used it in MainActivity.java in the onCreate method):

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
    if (checkSelfPermission(Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_DENIED || checkSelfPermission(Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_DENIED) {
        String[] permissions = {Manifest.permission.READ_PHONE_STATE, Manifest.permission.CALL_PHONE};
        requestPermissions(permissions, PERMISSION_REQUEST_READ_PHONE_STATE);
    }
}

The PERMISSION_REQUEST_READ_PHONE_STATE variable is used to determine which permission was asked for in the onRequestPermissionsResult method. Of course, if you don’t need to execute any logic depending on whether or not the user approved the permission, you can leave out this method:

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case PERMISSION_REQUEST_READ_PHONE_STATE: {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "Permission granted: " + PERMISSION_REQUEST_READ_PHONE_STATE, Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "Permission NOT granted: " + PERMISSION_REQUEST_READ_PHONE_STATE, Toast.LENGTH_SHORT).show();
            }

            return;
        }
    }
}

App in action

This is how the app looks like in action, tested on the emulator and call triggered by using Android Device Monitor in Android Studio:

Conclusion

In this post, I showed you how to make a native Android app that can block certain numbers from calling you. I pointed out the blocker that I was facing, and I’m still searching a solution to hide a native incoming call popup that still sometimes shows up for a brief second before the call gets rejected.

So, if you have any ideas, I’m open to suggestions ?

How to make a #native #Android app that can #block phone #calls https://t.co/NMYvOFlPO8

— Nikola Brežnjak (@HitmanHR) February 22, 2018

Android

How to simulate an incoming call or SMS to an emulator in Android Studio

In this quick tip, I’ll show you how easy it is to simulate an incoming call or SMS to an emulator in Android Studio.

TL;DR

In Android Studio, go to Tools->Android->Android Device Monitor

Select the Emulator on the left (you have to run it first – do that through Tools->Android->AVD Manager), then the Emulator Control tab, insert the number, select Voice or SMS and click the Call or Send button:

See both of these functions in action:

Conclusion

In this short tip, I showed you how easy it is to simulate an incoming call or SMS to an emulator in Android Studio.

Android Device Monitor allows you to set other stuff as well, like for example the current location, Network preferences (to test your app in not so ideal connectivity situations), etc.

Hope this helps ?

How to simulate an incoming call or SMS to an emulator in #Android Studio https://t.co/Ou9vRRV7RW

— Nikola Brežnjak (@HitmanHR) February 21, 2018

Projects

DevThink podcast

I’m super happy to announce that my friend (and coworker) Shawn Milochik and I started our very own podcast called DevThink.

In the DevThink podcast we discuss ideas and practices that worked for us in our software development journey so far. We’re also discussing the topics that we’re currently exploring and learning about.

Currently, we published two shows:

  • Is It Worth To Build A Profile On StackOverflow?
  • Dvorak #1 – Impressions After Using It for 2 Weeks

But, we’ve already recorded 8 additional shows that we’ll be publishing one per week.

Of course, feedback is greatly appreciated (be it good or bad)… We’re also open to suggestions for what you’d like to hear us discuss ?

Super happy to announce the DevThink podcast, brought to you by @ShawnMilo and yours truly https://t.co/VwgtRUdXkj

— Nikola Brežnjak (@HitmanHR) February 19, 2018

Angular 2, Go

JWT authentication in an Angular application with a Go backend

TL;DR

In this tutorial, I’m going to show you how to build a simple web app that handles authentication using JWT. The frontend will be written in Angular 5, and the backend will be in Go. I’ll cover some theory concepts along the way as well.

You can check out the final source code on Github.

JWT

JWT stands for JSON Web Token, and it is an encoded string that, for example, looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiYTFiMmMzIiwidXNlcm5hbWUiOiJuaWtvbGEifQ==.mKIuU0V0Bo99JU5XbeMe6g-Hrd3ZxJRlmdHFrEkz0Wk

If you split this string by ., you’ll get three separate strings:

  • header – contains encoded information about the token
  • payload – contains encoded data that is being transmitted between two parties
  • verification signature – used to verify that the data has not been changed

The official website says this about JWTs:

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

If you’re like

then I don’t blame you. So let’s define this in a bit more detail.

JSON Web Tokens are a way to communicate information between two parties securely. A claim is some data that is sent along with the token, like user_id.

Secure communication in this context refers to the fact that we can be certain that the information has not been tampered with, but it does not mean that it is hidden from a potential attacker. Actually, a potential attacker could read what is in JWT (so please don’t send any passwords as claims), but he wouldn’t be able to modify it and send it back in that form.

Based on the premise that a JWT can’t be tampered with, it is very useful for authentication. We can give a user a JWT that contains their userid, which can be stored locally and used to verify that requests are coming from an authenticated user.

JWTs are short, so you can easily send them as a POST parameter, HTTP header, or add it as a query string to a URL. You can store them in local storage and then send them with every request to the server, making sure the user is authorized.

It seems that a lot of people like and use them. However, I must note that a lot of security researchers frown upon this practice.

Learn by doing

It’s not necessary to know the intricate details of how JWTs work, to be able to use them, but it can sometimes give you this great feeling of awesomeness when you go an extra mile.

So, with that spirit in mind, we’re going to create our own JSON Web Token now ?

Payload

For example, say that I want to send the following data securely to someone:

{
    "user_id": "a1b2c3",
    "username": "nikola"
}

This is my payload. To add it to our JWT, we first need to base64 encode it. You can do this easily with JavaScript inside your browser’s developer tools (Console window) by using the btoa function:

btoa(JSON.stringify({
    "user_id": "a1b2c3",
    "username": "nikola"
}));

Or, with the ever so slightly popular Go, you would do it like this:

package main

import (
    "encoding/base64"
    "fmt"
)

func main() {
    data := `{"user_id":"a1b2c3","username":"nikola"}`
    uEnc := base64.URLEncoding.EncodeToString([]byte(data))
    fmt.Println(uEnc)
}

which then gives you:
eyJ1c2VyX2lkIjoiYTFiMmMzIiwidXNlcm5hbWUiOiJuaWtvbGEifQ==

⚠️ In the JavaScript example we had to use the JSON.stringify function first, as otherwise the resulting decoded string would just be [object Object].

We can decode the base64 encoded string by using the atob function in JavaScript:

atob('eyJ1c2VyX2lkIjoiYTFiMmMzIiwidXNlcm5hbWUiOiJuaWtvbGEifQ==')

or in Go:

uDec, _ := base64.URLEncoding.DecodeString("eyJ1c2VyX2lkIjoiYTFiMmMzIiwidXNlcm5hbWUiOiJuaWtvbGEifQ==")

The result, in both cases, is {"user_id":"a1b2c3","username":"nikola"}

Header

Next, we need to encode the header:

{
    "alg": "HS256",
    "typ": "JWT"
}

In JWT, the header actually comes before the payload. In the header, we are specifying that we created a JWT and that we used a certain hashing algorithm HS256. This particular algorithm will allow us to use a secret password. You could use the RSA SHA256 algorithm to use a private & public key pair instead. Here’s a good tutorial on the different hashing algorithms used in JWTs.

The header, base64 encoded, looks like this: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.

Verification signature

As the last step, we need to create the verification signature. We do this by joining the encoded header and payload string, separating them with .. After that, we need to apply the HS256 algorithm along with the secret password that is only known to the sender.

Our encoded header and payload look like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiYTFiMmMzIiwidXNlcm5hbWUiOiJuaWtvbGEifQ==

We’re going to use 42isTheAnswer as our password.

We don’t have this hashing function available in the browser, so we need to use Node to do it. First, install base64url by running: npm install base64url. If you’re new to Node, I recommend this tutorial.

Create a new JavaScript file with the following content:

var base64url = require('base64url');

var crypto    = require('crypto');
var message     = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiYTFiMmMzIiwidXNlcm5hbWUiOiJuaWtvbGEifQ==';
var key       = '42isTheAnswer';
var algorithm = 'sha256';
var hash, hmac;

hmac = crypto.createHmac(algorithm, key);
hmac.setEncoding('base64');
hmac.write(message);
hmac.end();
hash = hmac.read();

var final = base64url.fromBase64(hash);
console.log(final);

Or, in Go, you would use:

package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "fmt"
)

func main() {
    message := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiYTFiMmMzIiwidXNlcm5hbWUiOiJuaWtvbGEifQ=="
    sKey := "42isTheAnswer"

    key := []byte(sKey)
    h := hmac.New(sha256.New, key)
    h.Write([]byte(message))
    b := base64.URLEncoding.EncodeToString(h.Sum(nil))
    fmt.Println(string(a))
}

After you execute any of the scripts above, you should get this string:

mKIuU0V0Bo99JU5XbeMe6g-Hrd3ZxJRlmdHFrEkz0Wk

Now we add this string to the token from before (also separated by .) and we get:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiYTFiMmMzIiwidXNlcm5hbWUiOiJuaWtvbGEifQ==.mKIuU0V0Bo99JU5XbeMe6g-Hrd3ZxJRlmdHFrEkz0Wk

We can test this JWT on jwt.io:

Security

Our payload and header were just base64 encoded, which can just as easily be base64 decoded. So, how exactly is this secure then?

The important point is that it can’t be changed, since the verification signature is built using the header and the payload data, if either of those change, we won’t be able to verify the signature – so if somebody tampers with the JWT, we will know.

Since the payload data has been changed, the verification signature will no longer match, and there’s no way to forge the signature unless you know the secret that was used to hash it. When this JWT hits the server, it will know that it has been tampered with.

General remarks

If JWTs are used for Authentication, they will contain at least a user ID and an expiration timestamp.
This type of token is known as a Bearer Token. It identifies the user that owns it and defines a user session.

A Bearer Token is a signed temporary replacement for the username/password combination. The very first step for implementing JWT-based authentication is to issue a Bearer Token and give it to the user through the process of logging in.

The key property of JWTs is that to confirm if they are valid we only need to look at the token itself.

Demo apps

We’re going to build a simple full-stack app that will have a:

  • landing page
  • login page
  • members page
  • backend for authentication

Here’s how the authentication with JWTs works:

  • user submits the username and password to the server via the login page
  • server validates the sent data and creates a JWT token with a payload containing the user’s id and an expiration timestamp
  • server signs the Header and Payload with a secret password and sends it back to the user’s browser
  • browser takes the signed JWT and starts sending it with each HTTP request back to the server
  • signed JWT acts as a temporary user credential, that replaces the permanent credential (username and password)

Here’s what the server does upon receiving the JWT token:

  • the server checks the JWT signature and confirms that it’s valid
  • the Payload identifies a particular user via a user id
  • only the server has the secret password, and the server only gives out tokens to users that submit the correct password. Therefore, the server can be certain that this token was indeed given to this particular user by the server
  • the server proceeds with processing the HTTP request with this user’s credentials

Angular CLI with Bulma

Angular CLI is an awesome tool for Angular, and Bulma is a simple CSS framework that’s just a pure joy to work with.

Let’s start by generating a new project with Angular CLI (install it, in case you don’t have it already):

ng new jwt-auth

After this process is finished, run ng serve inside the jwt-auth folder, and you’ll have an app running at http://localhost:4200/:

Adding Bulma

Add this in the index.html file:

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css">
  <script defer src="https://use.fontawesome.com/releases/v5.0.0/js/all.js"></script>

Update app.component.html to:

<nav class="navbar">
  <div class="container">
    <div class="navbar-brand">
      <a class="navbar-item">
        JWT Angular Login
      </a>
    </div>

    <div id="navbarMenuHeroA" class="navbar-menu">
      <div class="navbar-end">
        <a class="navbar-item">
          Home
        </a>
        <a class="navbar-item">
          Login
        </a>
        <a class="navbar-item">
          Members
        </a>
        <a class="navbar-item">
          Logout
        </a>
      </div>
    </div>
  </div>
</nav>

<router-outlet></router-outlet>

<footer class="footer">
  <div class="container has-text-centered">
    <div class="content">
      From Croatia with ❤️
    </div>
  </div>
</footer>

If you take a look at the page now, you’ll see:

So, we have a header with links and a footer with simple text.

The <router-outlet></router-outlet> element will be used to serve other pages.

Now, let’s create three new components using Angular CLI:

ng g component home
ng g component login
ng g component members

One reason why Angular CLI is useful is that by generating the component, it creates 3 files for us and imports the component in the app.module.ts file:

create src/app/members/members.component.css (0 bytes)
create src/app/members/members.component.html (26 bytes)
create src/app/members/members.component.spec.ts (635 bytes)
create src/app/members/members.component.ts (273 bytes)
update src/app/app.module.ts (1124 bytes)

Now, let’s wire up the routes in app.module.ts:

const routes = [
    { path: 'login', component: LoginComponent },
    { path: 'members', component: MembersComponent },
    { path: '', component: HomeComponent },
    { path: '**', redirectTo: '' }
];

...
imports: [
    BrowserModule,
    RouterModule.forRoot(routes)
],

Set the links in the app.component.html using routerLink like this:

<a class="navbar-item" [routerLink]="['']">Home</a>
<a class="navbar-item" [routerLink]="['/login']">Login</a>
<a class="navbar-item" [routerLink]="['/members']">Members</a>

If all is fine, you should see this in your browser:

Login

Replace the contents of the login.component.html with:

<section class="hero">
  <div class="hero-body has-text-centered">
    <form [formGroup]="form">
      <div class="columns">
        <div class="column"></div>

        <div class="column is-3">
          <div class="field">
            <label class="label is-pulled-left">Email</label>
            <div class="control">
              <input class="input" type="text" placeholder="[email protected]" formControlName="email" name="email">
            </div>
          </div>
        </div>

        <div class="column"></div>
      </div>

      <div class="columns">
        <div class="column"></div>

        <div class="column is-3">
          <div class="field">
            <label class="label is-pulled-left">Password:</label>
            <div class="control">
              <input class="input" type="password" formControlName="password" name="password">
            </div>

            <br>
            <br>
            <a class="button is-primary is-medium is-fullwidth" (click)='login()'>Login</a>
          </div>
        </div>

        <div class="column"></div>
      </div>
    </form>
  </div>
</section>

and add login.component.ts with:

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';

@Component({
    selector: 'app-login',
    templateUrl: './login.component.html',
    styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
    form: FormGroup;

    constructor(private fb: FormBuilder) {
        this.form = this.fb.group({
            email: ['', Validators.required],
            password: ['', Validators.required]
        });
    }

    ngOnInit() {
    }

    login() {
        console.log('Clicked the Login button');
    }
}

You may notice that we used a bunch of imports from @angular/forms, so we also need to add it in app.module.ts in the imports array:

...
imports: [
    BrowserModule,
    FormsModule,
    ReactiveFormsModule,
    RouterModule.forRoot(routes)
],
...

Before we go to the actual authentication section, let’s just fix the Home and Members area slightly.

Home and Members

Update the HTML files to the following content:

home.component.html:

<section class="hero" id="hero">
  <div class="hero-head"></div>
  <div class="hero-body">
    <div class="container has-text-centered">
      <h1 class="is-1 title">
        Welcome to JWT Angular Auth!
      </h1>
    </div>
  </div>
</section>

members.component.html:

<section class="hero" id="hero">
  <div class="hero-head"></div>
  <div class="hero-body">
    <div class="container has-text-centered">
      <h1 class="is-1 title">
        Members area
      </h1>
    </div>
  </div>
</section>

Go

Our backend is written in Golang, and it looks like this:

package main

import (
    "encoding/json"
    "errors"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "strings"
    "time"

    jwt "github.com/dgrijalva/jwt-go"
    "github.com/rs/cors"
)

const (
    PORT   = "1337"
    SECRET = "42isTheAnswer"
)

type JWTData struct {
    // Standard claims are the standard jwt claims from the IETF standard
    // https://tools.ietf.org/html/rfc7519
    jwt.StandardClaims
    CustomClaims map[string]string `json:"custom,omitempty"`
}

type Account struct {
    Email    string  `json:"email"`
    Balance  float64 `json:"balance"`
    Currency string  `json:"currency"`
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", hello)
    mux.HandleFunc("/login", login)
    mux.HandleFunc("/account", account)

    handler := cors.Default().Handler(mux)

    log.Println("Listening for connections on port: ", PORT)
    log.Fatal(http.ListenAndServe(":"+PORT, handler))
}

func hello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello from Go!")
}

func login(w http.ResponseWriter, r *http.Request) {
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        log.Println(err)
        http.Error(w, "Login failed!", http.StatusUnauthorized)
    }

    var userData map[string]string
    json.Unmarshal(body, &userData)

    // Demo - in real case scenario you'd check this against your database
    if userData["email"] == "[email protected]" && userData["password"] == "admin123" {
        claims := JWTData{
            StandardClaims: jwt.StandardClaims{
                ExpiresAt: time.Now().Add(time.Hour).Unix(),
            },

            CustomClaims: map[string]string{
                "userid": "u1",
            },
        }

        token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
        tokenString, err := token.SignedString([]byte(SECRET))
        if err != nil {
            log.Println(err)
            http.Error(w, "Login failed!", http.StatusUnauthorized)
        }

        json, err := json.Marshal(struct {
            Token string `json:"token"`
        }{
            tokenString,
        })

        if err != nil {
            log.Println(err)
            http.Error(w, "Login failed!", http.StatusUnauthorized)
        }

        w.Write(json)
    } else {
        http.Error(w, "Login failed!", http.StatusUnauthorized)
    }
}

func account(w http.ResponseWriter, r *http.Request) {
    authToken := r.Header.Get("Authorization")
    authArr := strings.Split(authToken, " ")

    if len(authArr) != 2 {
        log.Println("Authentication header is invalid: " + authToken)
        http.Error(w, "Request failed!", http.StatusUnauthorized)
    }

    jwtToken := authArr[1]

    claims, err := jwt.ParseWithClaims(jwtToken, &JWTData{}, func(token *jwt.Token) (interface{}, error) {
        if jwt.SigningMethodHS256 != token.Method {
            return nil, errors.New("Invalid signing algorithm")
        }
        return []byte(SECRET), nil
    })

    if err != nil {
        log.Println(err)
        http.Error(w, "Request failed!", http.StatusUnauthorized)
    }

    data := claims.Claims.(*JWTData)

    userID := data.CustomClaims["userid"]

    // fetch some data based on the userID and then send that data back to the user in JSON format
    jsonData, err := getAccountData(userID)
    if err != nil {
        log.Println(err)
        http.Error(w, "Request failed!", http.StatusUnauthorized)
    }

    w.Write(jsonData)
}

func getAccountData(userID string) ([]byte, error) {
    output := Account{"[email protected]", 3.14, "BTC"}
    json, err := json.Marshal(output)
    if err != nil {
        return nil, err
    }

    return json, nil
}

⚠️ I’m not a Go expert (yet), so this code would be written way more idiomatic by someone who’s using the language longer. But, when confronted with such thoughts yourself, remember this: “Perfect is the enemy of good”, and it’s way better to learn by doing and getting stuff ‘out there’ and getting feedback, than to ‘wait x months until you master a language’.

So, here goes my best attempt at explaining what the code does, from top to bottom:

package

package main

First, we have the package statement. Every Go program must have a main package.

imports

import (
    "encoding/json"
    "errors"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "strings"
    "time"

    jwt "github.com/dgrijalva/jwt-go"
    "github.com/rs/cors"
)

Then we have the imports. All of the imports, except for jwt and cors are from the standard Go library. If you’re using an editor like VS Code, or an IDE like GoLand, then these imports are added automatically as you save your code.

One thing I love about Go is the auto code format, so finally, some language where there will be no debate about whether the brackets in ifs go on the same line or in the next. Consistency FTW!

constants

const (
    PORT   = "1337"
    SECRET = "42isTheAnswer"
)

Then we have two constants: PORT and SECRET. It is not a practice in Go to have all uppercase letters for constants, but I’m blindly sticking to that habit it seems.

structs

type JWTData struct {
    // Standard claims are the standard jwt claims from the IETF standard
    // https://tools.ietf.org/html/rfc7519
    jwt.StandardClaims
    CustomClaims map[string]string `json:"custom,omitempty"`
}

type Account struct {
    Email    string  `json:"email"`
    Balance  float64 `json:"balance"`
    Currency string  `json:"currency"`
}

Next, we have two structs: JWTData and Account. The JWTData struct, along with some standard fields (claims) has an additional CustomClaims map, that can hold key-value pairs of type string. We will use this data type to add our own custom claims (userid).

The Account struct is used as an example structure for responding to the logged in user once he’s logged in and comes to the Members page. It contains the Email, Balance and Currency fields.

main

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", hello)
    mux.HandleFunc("/login", login)
    mux.HandleFunc("/account", account)

    handler := cors.Default().Handler(mux)

    log.Println("Listening for connections on port: ", PORT)
    log.Fatal(http.ListenAndServe(":"+PORT, handler))
}

In the main function we ‘register’ the handlers for our API. If we presume that this Go program would be running on a domain http://api.boringcompany.com, then the request to that URL would be handled by the hello function that we’ll show below. If the request is sent to the http://api.boringcompany.com/login URL, it would be handled by the login function that we’ll show below, etc. Finally, we print the message via the log and start the server with the http.ListenAndServe function.

The CORS handler is necessary only when developing locally. If you’ll do that, then I also recommend the CORS plugin for the browser you’re using (here is the one that I use for Chrome).

hello handler

func hello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello from Go!")
}

This is a simple function that outputs Hello from Go! back to the user when he hits our main API URL.

login handler

func login(w http.ResponseWriter, r *http.Request) {
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        log.Println(err)
        http.Error(w, "Login failed!", http.StatusUnauthorized)
    }

    var userData map[string]string
    json.Unmarshal(body, &userData)

    // Demo - in real case scenario you'd check this against your database
    if userData["email"] == "[email protected]" && userData["password"] == "admin123" {
        claims := JWTData{
            StandardClaims: jwt.StandardClaims{
                ExpiresAt: time.Now().Add(time.Hour).Unix(),
            },

            CustomClaims: map[string]string{
                "userid": "u1",
            },
        }

        token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
        tokenString, err := token.SignedString([]byte(SECRET))
        if err != nil {
            log.Println(err)
            http.Error(w, "Login failed!", http.StatusUnauthorized)
        }

        json, err := json.Marshal(struct {
            Token string `json:"token"`
        }{
            tokenString,
        })

        if err != nil {
            log.Println(err)
            http.Error(w, "Login failed!", http.StatusUnauthorized)
        }

        w.Write(json)
    } else {
        http.Error(w, "Login failed!", http.StatusUnauthorized)
    }
}

In the login function we first read the body of the request and parse out the email and password parameters. We then check this email/password combination and make sure it’s correct. Of course, for demo purposes it was done like that in the code; in real case scenario, you’d check this against your database most probably.

If the user credentials are correct, we prepare the claims where we use the standard ExpiresAt claim, and also we add our own custom claim of userid with the value u1.

Next, we use the jwt.NewWithClaims function to sign the header and the payload with the HS256 hashing algorithm and we use the SECRET as a key for that. Finally, we then return this token to the user in JSON format.

Otherwise, if any errors happen, we send back the unauthorized status with a failure message.

account handler

func account(w http.ResponseWriter, r *http.Request) {
    authToken := r.Header.Get("Authorization")
    authArr := strings.Split(authToken, " ")

    if len(authArr) != 2 {
        log.Println("Authentication header is invalid: " + authToken)
        http.Error(w, "Request failed!", http.StatusUnauthorized)
    }

    jwtToken := authArr[1]

    claims, err := jwt.ParseWithClaims(jwtToken, &JWTData{}, func(token *jwt.Token) (interface{}, error) {
        if jwt.SigningMethodHS256 != token.Method {
            return nil, errors.New("Invalid signing algorithm")
        }
        return []byte(SECRET), nil
    })

    if err != nil {
        log.Println(err)
        http.Error(w, "Request failed!", http.StatusUnauthorized)
    }

    data := claims.Claims.(*JWTData)

    userID := data.CustomClaims["userid"]

    // fetch some data based on the userID and then send that data back to the user in JSON format
    jsonData, err := getAccountData(userID)
    if err != nil {
        log.Println(err)
        http.Error(w, "Request failed!", http.StatusUnauthorized)
    }

    w.Write(jsonData)
}

func getAccountData(userID string) ([]byte, error) {
    output := Account{"[email protected]", 3.14, "BTC"}
    json, err := json.Marshal(output)
    if err != nil {
        return nil, err
    }

    return json, nil
}

In the account function we first read the Authorization header and take out the token. Then we make sure the token is valid and has not been tampered with, and we parse out the claims by using the jwt.ParseWithClaims function.

With the userID claim we fetch some data (using the getAccountData function) and then send that data back to the user in the JSON format.

Running the Go app

You can run this app locally on your computer with go run main.go. Of course, you need to have Go installed. You can check how to do that in this tutorial.

Finishing up the Angular frontend

Now let’s switch back to our Angular project and make actual requests to our API.

Auth service

Using Angular CLI, execute the following command in your terminal:

ng g service auth 

This now created two files for us:

create src/app/auth.service.spec.ts (362 bytes)
create src/app/auth.service.ts (110 bytes)

Copy the following code to the auth.service.ts file:

import { Injectable } from '@angular/core';
import { RequestOptions, Response } from '@angular/http';

import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Router } from '@angular/router';

@Injectable()
export class AuthService {

    API_URL = 'http://localhost:1337';
    TOKEN_KEY = 'token';

    constructor(private http: HttpClient, private router: Router) { }

    get token() {
        return localStorage.getItem(this.TOKEN_KEY);
    }

    get isAuthenticated() {
        return !!localStorage.getItem(this.TOKEN_KEY);
    }

    logout() {
        localStorage.removeItem(this.TOKEN_KEY);
        this.router.navigateByUrl('/');
    }

    login(email: string, pass: string) {
        const headers = {
            headers: new HttpHeaders({ 'Content-Type': 'application/json', 'Cache-Control': 'no-cache' })
        };

        const data = {
            email: email,
            password: pass
        };

        this.http.post(this.API_URL + '/login', data, headers).subscribe(
            (res: any) => {
                localStorage.setItem(this.TOKEN_KEY, res.token);

                this.router.navigateByUrl('/members');
            }
        );
    }

    getAccount() {
        return this.http.get(this.API_URL + '/account');
    }
}

AuthService consists of these functions:

  • login – we send the email/password that the user enters to the server and upon success, we store the token in local storage. Please note the warning that I gave in the theory part of this tutorial.
  • logout – we delete the token from local storage and redirect the user to the landing page
  • token – returns the token from local storage
  • isAuthenticated – returns true/false if the token exists in the local storage
  • getAccount – requests the user data and returns a promise

login component

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AuthService } from '../auth.service';

@Component({
    selector: 'app-login',
    templateUrl: './login.component.html',
    styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
    form: FormGroup;

    constructor(private fb: FormBuilder, private authService: AuthService) {
        this.form = this.fb.group({
            email: ['', Validators.required],
            password: ['', Validators.required]
        });
    }

    ngOnInit() {
    }

    login() {
        const val = this.form.value;

        if (val.email && val.password) {
            this.authService.login(val.email, val.password);
        }
    }
}

The most important part is the login function that calls the AuthService login function passing it email and password. We use the FormBuilder in Angular to access form fields that in the HTML code look like this:

<section class="hero">
  <div class="hero-body has-text-centered">
    <form [formGroup]="form">
      <div class="columns">
        <div class="column"></div>

        <div class="column is-3">
          <div class="field">
            <label class="label is-pulled-left">Email</label>
            <div class="control">
              <input class="input" type="text" placeholder="[email protected]" formControlName="email" name="email">
            </div>
          </div>
        </div>

        <div class="column"></div>
      </div>

      <div class="columns">
        <div class="column"></div>

        <div class="column is-3">
          <div class="field">
            <label class="label is-pulled-left">Password:</label>
            <div class="control">
              <input class="input" type="password" formControlName="password" name="password">
            </div>

            <br>
            <br>
            <a class="button is-primary is-medium is-fullwidth" (click)='login()'>Login</a>
          </div>
        </div>

        <div class="column"></div>
      </div>
    </form>
  </div>
</section>

Notice <form [formGroup]="form"> and formControlName="email". In Angular we register the click handler like this: (click)='login()'.

members component

Members component is pretty simple:

<section class="hero" id="hero">
  <div class="hero-head"></div>
  <div class="hero-body">
    <div class="container has-text-centered">
      <h1 class="is-1 title">
        Members area
      </h1>

      <p>Email:
        <b>{{accountData?.email}}</b>
      </p>
      <p>Balance:
        <b>{{accountData?.balance}} {{accountData?.currency}}</b>
      </p>
    </div>
  </div>
</section>

We use it to show some data that we’ll get from the API. Very important part is the use of ? – this instructs Angular to not throw an error while it’s rendering the template in case the data doesn’t yet exist (as it will be the case since we’re fetching this data from an API).

The controller code looks like this:

import { Component, OnInit } from '@angular/core';
import { AuthService } from '../auth.service';
import { Router } from '@angular/router';

@Component({
    selector: 'app-members',
    templateUrl: './members.component.html',
    styleUrls: ['./members.component.css']
})
export class MembersComponent implements OnInit {
    accountData: any;
    constructor(private authService: AuthService, private router: Router) { }

    ngOnInit() {
        this.authService.getAccount().subscribe(
            (res: any) => {
                this.accountData = res;
            }, (err: any) => {
                this.router.navigateByUrl('/login');
            }
        );
    }

}

When the component loads, we send a request to the API, and upon the success, we save the data in the accountData variable that we then use in the template as we saw previously. If an error occurs, we forward the user to the landing page.

app.component.html

<nav class="navbar">
  <div class="container">
    <div class="navbar-brand">
      <a class="navbar-item">
        JWT Angular Login
      </a>
    </div>

    <div id="navbarMenuHeroA" class="navbar-menu">
      <div class="navbar-end">
        <a class="navbar-item" [routerLink]="['']">
          Home
        </a>
        <a class="navbar-item" [routerLink]="['/login']" *ngIf="!authService.isAuthenticated">
          Login
        </a>
        <a class="navbar-item" [routerLink]="['/members']" *ngIf="authService.isAuthenticated">
          Members
        </a>
        <a class="navbar-item" *ngIf="authService.isAuthenticated" (click)="authService.logout()">
          Logout
        </a>
      </div>
    </div>
  </div>
</nav>

<router-outlet></router-outlet>

<footer class="footer">
  <div class="container has-text-centered">
    <div class="content">
      From Croatia with ❤️
    </div>
  </div>
</footer>

The important part to note here is the use of *ngIf="!authService.isAuthenticated" to show/hide the navigation links based on the fact if the user is logged in or not.

The only thing we need to do in the ‘code’ file is to make sure we import the AuthService via the constructor: constructor(private authService: AuthService) { }.

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';

import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { LoginComponent } from './login/login.component';
import { MembersComponent } from './members/members.component';

import { AuthService } from './auth.service';
import { AuthInterceptorService } from './auth-interceptor.service';
import { CanActivateViaAuthGuard } from './can-activate-via-auth.guard';

const routes = [
    { path: 'login', component: LoginComponent },
    {
        path: 'members',
        component: MembersComponent,
        canActivate: [
            CanActivateViaAuthGuard
        ]
    },
    { path: '', component: HomeComponent },
    { path: '**', redirectTo: '' }
];

@NgModule({
    declarations: [
        AppComponent,
        HomeComponent,
        LoginComponent,
        MembersComponent
    ],
    imports: [
        BrowserModule,
        FormsModule,
        ReactiveFormsModule,
        HttpClientModule,
        RouterModule.forRoot(routes)
    ],
    providers: [
        AuthService,
        {
            provide: HTTP_INTERCEPTORS,
            useClass: AuthInterceptorService,
            multi: true
        },
        CanActivateViaAuthGuard
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }

This file imports all the components that we’re using. As you can see, in the ‘declarations’ we list the components that we’re using, the imports contain imported components for working with forms or sending HTTP requests.

guards and interceptors

Finally, you may notice something new in the providers array, where with the usual AuthService we have two additional things defined (an interceptor service and an auth guard):

{
    provide: HTTP_INTERCEPTORS,
    useClass: AuthInterceptorService,
    multi: true
},
CanActivateViaAuthGuard

The interceptor service has one task: to intercept every request that goes from the app and add the token to that request in its header:

import { Injectable, Injector } from '@angular/core';
import { HttpInterceptor } from '@angular/common/http';
import { AuthService } from './auth.service';

@Injectable()
export class AuthInterceptorService implements HttpInterceptor {

    constructor(private injector: Injector) { }

    intercept(req, next) {
        const authService = this.injector.get(AuthService);
        const authRequest = req.clone({
            // tslint:disable-next-line:max-line-length
            headers: req.headers.set('Authorization', 'Bearer ' + authService.token)
        });

        return next.handle(authRequest);
    }
}

THe guard is also simple and it’s defined like this:

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { AuthService } from './auth.service';

@Injectable()
export class CanActivateViaAuthGuard implements CanActivate {
    constructor(private authService: AuthService) {

    }

    canActivate() {
        return this.authService.isAuthenticated;
    }
}

So, we define the so-called ‘canActivate’ guard which we use when we want to prevent the user from going to some route in the app to which he doesn’t have the access. We use this guard in the route definition to prevent the user from going to the /members link if he’s not logged in:

const routes = [
    { path: 'login', component: LoginComponent },
    {
        path: 'members',
        component: MembersComponent,
        canActivate: [
            CanActivateViaAuthGuard
        ]
    },
    { path: '', component: HomeComponent },
    { path: '**', redirectTo: '' }
];

When you have all of this wired up (and a Go program running locally), you should see:

Conclusion

We’ve covered a lot of ground in this long post:

  • we learned a bit about how JWTs work
  • we created our own JWT with code examples in JavaScript and Go
  • we made a full-fledged app with Angular 5 on the frontend and Go on the backend that uses JWTs for authentication
  • we made use of Angulars Guards and Interceptors

I hope this was enough to get you started and build upon this example.

If you have any questions, feel free to reach out in the comments.

#JWT authentication in an #Angular application with a #Go backend https://t.co/UpZgaOcJUb

— Nikola Brežnjak (@HitmanHR) February 19, 2018

Ionic3, JavaScript

How to create an Android Cordova plugin for showing Toast popups

TL;DR

In this post, I’m going to show you step by step how to build a Cordova plugin for Android that shows a native Toast popup.

You can check out the source code of the plugin here and the source of the demo Ionic app that’s using this plugin here.

In case you’re looking for the guide on how to write the Cordova plugin for the iOS platform, I wrote about it here.

plugin.xml

We start the process of plugin building by creating the plugin.xml file:

<?xml version='1.0' encoding='utf-8'?>
<plugin id="cordova-android-toast" version="1.0.0" xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
    <name>AndroidToast</name>

    <description>Android Toast Plugin</description>
    <license>Apache 2.0</license>
    <keywords>android, toast</keywords>

    <engines>
      <engine name="cordova" version=">=3.0.0" />
    </engines>

    <js-module name="AndroidToast" src="www/AndroidToast.js">
        <clobbers target="AndroidToast" />
    </js-module>

    <platform name="android">
        <config-file target="config.xml" parent="/*">
            <feature name="AndroidToast">
                <param name="android-package" value="com.nikolabreznjak.AndroidToast" />
            </feature>
        </config-file>

        <source-file src="src/android/AndroidToast.java" target-dir="src/com/nikola-breznjak/android-toast" />
    </platform>
</plugin>

In this file you basically define:

  • the platform this plugin supports (<platform name="android">)
  • where the source files of your plugin will be (source-file elements)
  • where is the JavaScript file that will be the bridge from Cordova to native code (js-module tag src property)
  • what will be the plugin’s name by which you’ll reference it in the Cordova/Ionic code (<clobbers target="AndroidToast" />)

www/AndroidToast.js

Next comes the so-called ‘bridge’ file that connects the native and JavaScript side. It is common to put this file in the www folder. The contents of this file is as follows:

var exec = cordova.require('cordova/exec');

var AndroidToast = function() {
    console.log('AndroidToast instanced');
};

AndroidToast.prototype.show = function(msg, onSuccess, onError) {
    var errorCallback = function(obj) {
        onError(obj);
    };

    var successCallback = function(obj) {
        onSuccess(obj);
    };

    exec(successCallback, errorCallback, 'AndroidToast', 'show', [msg]);
};

if (typeof module != 'undefined' && module.exports) {
    module.exports = AndroidToast;
}

We created the AndroidToast function, which in other programming languages would basically be a class, because we added the show function on its prototype. The show function, via Cordova’s exec function, registers the success and error callbacks for the AndroidToast class and the show method on the native side that we’ll show now shortly. Also, we pass in the msg variable as an array to the native show function.

src/android/AndroidToast.java

The ‘native’ code is written in Java:

package com.nikolabreznjak;

import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;
import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONException;
import android.content.Context;
import android.widget.Toast;

public class AndroidToast extends CordovaPlugin {
    @Override
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
        if ("show".equals(action)) {
            show(args.getString(0), callbackContext);
            return true;
        }

        return false;
    }

    private void show(String msg, CallbackContext callbackContext) {
        if (msg == null || msg.length() == 0) {
            callbackContext.error("Empty message!");
        } else {
            Toast.makeText(webView.getContext(), msg, Toast.LENGTH_LONG).show();
            callbackContext.success(msg);
        }
    }
}

In the implementation file, we define our functions. We only have the show and execute functions in our example. When writing Cordova plugins for Android, every function from the bridge file has to call the exec function, which then calls the execute function on the native side. Then, based on the action parameter, we decide which function needs to be called. In our case, if we determine that the show action was called, we pass through the arguments to the private show function, which then uses the native Toast.makeText function to display the Toast message.

When displaying the Toast, our show method needs access to the app’s global Context, which can be obtained using the webView object that we get from extending CordovaPlugin. This represents the running Cordova app, and we can get the global Context from there using: webView.getContext(). Other parameters to the makeText function define the text that we want to show and the duration of how long we want to show it.

At this point you could customize the toast as much as the native Toast component allows, there are no restrictions even though this is used via Cordova as a kind of a ‘wrapper’. Some additional stuff that you could do is listed in the official documentation.

package.json

In earlier versions of Cordova, this file wasn’t required. You can generate it automatically with the help of the plugman package (install it with npm install plugman -g in case you don’t have it):

plugman createpackagejson /path/to/your/plugin.

If you’re in the plugin folder, then the command is: plugman createpackagejson .. The package.json file in my case now looks like this:

{
    "name": "cordova-android-toast",
    "version": "1.0.0",
    "description": "Android Toast Plugin",
    "cordova": {
        "id": "cordova-android-toast",
        "platforms": [
            "android"
        ]
    },
    "keywords": [
        "android",
        "toast",
        "ecosystem:cordova",
        "cordova-android"
    ],
    "engines": [{
        "name": "cordova",
        "version": ">=3.0.0"
    }],
    "author": "Nikola Brežnjak<[email protected]> (http://www.nikola-breznjak.com/blog)",
    "license": "Apache 2.0"
}

Using the plugin

First, you need to install it. If you’re using Ionic:

ionic cordova plugin add cordova-android-toast

If you’re using Cordova:

cordova plugin add cordova-android-toast

In the code, you would show the toast message like this:

constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen) {
        platform.ready().then(() => {
            var androidToast = new AndroidToast();
            androidToast.show(
                'This is some nice toast popup!',
                function(msg) {
                    console.log(msg);
                },
                function(err) {
                    console.log(err);
                }
            );

        });
    }

If you’re using the newest (currently 3) version of Ionic, then you would have to add this line: declare var AndroidToast: any; after the imports in app.component.ts (or any other file where you’ll use the plugin) before actually using it. If you’re using Ionic 1, you don’t need to do this.

As a final note, make sure you’re accessing the plugin after the platform.ready() fires, just so you make sure that the plugin is ready for use.

Running the demo locally

Clone this repo:

git clone https://github.com/Hitman666/cordova-android-toast.git

CD into the cloned project

cd cordova-android-toast

Install the dependencies:

npm install && bower install

Add the Android platform (please note that this process may take a while to complete):

ionic cordova platform add android

Add the plugin:

ionic cordova plugin add cordova-android-toast

Run the project on the device (if you have it connected):

ionic cordova run android

Run the project on the emulator:

ionic cordova emulate android

You should see something like this once the app runs on your device:

Conclusion

I hope this post gave you enough information so that you’ll be dangerous enough to go and fiddle with the Android Cordova plugin building yourself ?

If you have any questions, feel free to reach out.

Hacker Games

Help me create a list of Hacker Challenge Games

TL; DR

I’m looking to create a list of so-called Hacker Challenge Games that were popular in the last century. Since a lot of them are no longer available, some may light up like a Christmass tree when they see their favorite ‘games’ from a time that now seems soooo far away. ?

Come and help me gather them in the comments or, if you feel like it, submit a Pull Request ?

Nostalgia

Picture this: it’s somewhere around Y2K, and everyone is talking about how those big white boxes aka computers)are gonna lose it and that the world is better of without them (ah, dear haters…).

It so happened that I was fortunate enough to own one of those boxes. Not only that, but one dad’s friend got us all set up for this thing called The Internet. Now, mind you, I used that good ‘ol awesome cool sound making 56 Kbps modem.

Anyways, I honestly don’t remember how I found about these ‘games’. Maybe I wanted to be a hacker after watching the cult movie with Angelina Jolie, so I Yahooed (no wonder that never stick) ‘hackers’ and there I was.

But what I do remember is how many countless hours I spent solving these challenges.

This was not only great because it strengthened my ability for problem-solving and never giving up, but also it made me learn new stuff like Cryptography, Steganography, Web, Cracking even ?. Of course, it also made me learn various programming languages as some tasks were designed like that.

I was so into it, that I even made my own version – Hitman’s challenge as a bachelor thesis. Those interested in it, can read it here (note: it’s in Croatian).

Ah, don’t ask, I think the code is awful and I’m ashamed of it now. But, a wise friend told me that that means that I’ve probably progressed as a developer, and progress is always a good thing. He even went so far to say that if you look back at your code from six months ago and you’re happy with it, you probably haven’t grown as a dev… No worries, with all the new frameworks popping up these days, you can stay busy just evaluating them, but that’s a rant for some other post.

A bit later I earned my masters degree in computing, got a job and started writing REAL software. I’ve officially ‘grown up’. Little Prince would be disappointed. ?

However, I loved it and still to this day remember those days with joy and nostalgia. Just the other day I got this idea of checking if these old sites still exist. I was sad to find out they’re no longer around. R.I.P.

So now, please help me remember what all was ‘out there’ back in the days. But also, please by all means – list any new that are popular these days.

The ‘Hacker’ games

Here I’m listing few that I remember:

Still LIVE
+ http://www.try2hack.lt/en/
+ http://bright-shadows.net/

R.I.P.
+ hackits.de
+ hackerlab.org
+ slyfx.com
+ gild.com
+ arcanum.co.nz

Back in the day, there was this one web directory that listed all of these sites, and even though the site is still up (http://hackergames.net/), it doesn’t seem like it’s working anymore.

Sure, nowadays we have things like:

  • https://projecteuler.net/
  • https://adventofcode.com/
  • https://www.topcoder.com/

but these sites just offer the programming challenges as far as I know.

Conclusion

I hope I sparked some joy with this post and am really looking forward to seeing what you come up with in the comments.

As for some lingering idea: oh wouldn’t it be great if we could make an open source hacker challenge game #wishfullThinking

Miscellaneou$

Learn Git fast as if your job depends on it

Update: Fancy a video course? I made my first one on Skillshare, go check it out.

TL;DR

In this post, with a grain of humor, I’m going to show you the minimum number of Git commands that you need to learn in order to start being productive and get that code from your machine to the company Git server. Also, I’ll cover a bit of theory and Git installation as well.

Disclaimer

⚠️ Nobody in this story, and no outfit or corporation, thank God, is based upon an actual person or outfit in the real world. But I can tell you this; as my journey through the software jungle progressed, I came to realize that, by comparison with the reality, my story was as tame as a holiday postcard. ~ John le Carré

Setting the stage

So, let’s set a scene; it’s your first day as a junior developer straight out of college. You get to work, greet your superior that’s supposed to be responsible for you, and since he’s too busy with putting down some fire that just popped up, he, conversely, while you’re trying to keep up walking aside him tells you something like:

I’ve set the Git access for you to my website project.

You have a ticket in our project management tool assigned to you. No worries, it’s just some simple stuff that needs to be changed.

Just check the email details, and you can start immediately.

I’m aware that you may not be familiar with Git. I’m not trying to be an asshole, but just deal with it, learn it, OK? Don’t they, like, teach you kids nothing in school these days?

Ah, I gotta run.

Oh, and one more thing, look up branching and do not EVER, and I repeat – EVER, commit to master; do a PR instead! Good luck and see ya later!

OK. You sit down at your cubicle, open up your email client, log in, and there you see the email (without the subject line, of course) with the contents:

Here’s a link to the Git repo on Github.

Good luck,
Mr. Superior

Reality sets in

⚠️ This, in and of itself is a poor onboarding if you ask me, but I personally have seen it way too many times, so here are the things you need to do to get productive fast and learn the ropes as fast as you can.

So now you’re, in lack of better words, screwed (you know the exact word, but we’re not supposed to be using profanity here, ae?).

You’re lost. You heard about Git before, but you never used it. Now you’re kicking yourself for not learning it properly yourself. But, it is what it is, and since you’re really determined to make it in this ‘programming’ world, you’re ready to do what it takes.

I would question the fact that if you are ‘so into it’, how come you haven’t learned it yet, but as Mr. Superior said, I’m not trying to be an asshole ?

Therefore, buckle up young padawan, you’re in for a ride!

What is this Git thing?

First, let me start off with a few problems:

  • Say you’re writing a seminar and you make some changes, save them, go out of the text editor, come back, and you want to see all of your changes that you did last in the file. Or, for example, you want to undo some changes that you did last night (writing a seminar after returning from a party is a noble act, but not a very smart one!).
  • While writing this seminar you create several versions of the same file and you don’t even know what’s changed in which version; you would really love to know what was, for example, changed in this current version based on the, say, a version from a few days ago.
  • You’re working on a group project and someone deletes your changes ‘by accident’.

VCS (Version Control Systems) tends to solve these problems. And, let me start off by saying that no, you don’t need to use VCS only for writing code (thus the seminar example). You can use it for any textual project. One of VCS solutions is Git. One other popular VCS, that’s still used to some extent today is Subversion. You can see a detailed list of Version Control Systems here.

Git was made by the ever so slightly genius Linus Torvalds, the father of Linux:

Git is a distributed version control system (VCS).

Version control means that you use it to store versions of your documents. True, it seems it’s very popular among developers, but designers and other people (that are a bit more tech savvy) also use it to track versions of their files.

This whole distributed part is one of the things that makes it different from other VCS. Rather than have only one single place for the full version history of the software, as is common in once-popular VCS like CVS or Subversion, in Git every developer’s working copy is also a repository that can contain the full history of all changes.

OK, but what is Github?

Something like Dropbox, but customized for Git.

Github is a hosting service for Git projects, with some additional batteries included that we’re not going to get into at this point, as you’re short on time. Think of it as a place where you store your Git ‘stuff’.

Installing Git

To install Git, go to the official website and download the executable for your machine. I’m not going into the installation details as they’re really just a bunch of Next, Next, Next, yes to malware, Finish set of steps. I’m kidding about the malware OFC.

To make sure your installation was successful, run the following command in your Terminal/Command prompt:

git --version

You should get something like:

git version 2.10.1

Fetching the repo

OK, so let’s finally get some action. In your email, you received an URL to the Git repository. It was this: https://github.com/Hitman666/MrSupervisorWebsite. You click the link, and you see something like this:

Click the Clone or download button, make sure you see the Use SSH in the top right corner of a popup and copy the link.

To get this repository to your computer, you have to execute the following command in your Terminal/Command prompt (I’ll use Terminal in the rest of the post):

git clone https://github.com/Hitman666/MrSupervisorWebsite.git

The output of the command should look something like this:

Cloning into 'MrSupervisorWebsite'...
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 7 (delta 0), reused 7 (delta 0), pack-reused 0
Receiving objects: 100% (7/7), done.

Checking out the code

Now, go into the newly created MrSupervisorWebsite folder and open it in your editor.

You should see three files: index.htmnl, style.css, and README.md.

You quickly go and click to open the README.md file, but all you see is this:

# MrSupervisorWebsite

Hmm, so, no help there.

You open up the index.html file and you see:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>MrSupervisorWebsite</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <h1 class="title">Welcome to MrSupervisorWebsite!</h1>
</body>
</html>

OK, finally something that you’re familiar with! You also open up the style.css file, and you see:

.title {
    color: blue;
}

Next, you take a look at the ticket assigned to you in the project management tool that Mr.Superior mentioned, and it looks something like:

You take a deep breath, finally something that you know how to handle! ?

You’re a bit taken aback by the ‘manly color’, so you go and ask Google:

You pick the 4872A2, and you’re happy. You think to yourself: OK, now I just need to change this color in the style.css file, somehow add this to this Git thing and ‘publish’ it.

You’re right, but not so fast! You remember that note from Mr.Supervisor that you shouldn’t commit to master, EVER?

Creating a new branch

So, what is this master anyway? You go online, and you find that:

Git has branches, and its main one is called master. You can create as many of them as you want, based on any other existing one.

Also, you read a bit about the PR, and you learn that:

PR is short for Pull Request. Pull requests let you tell others about changes you’ve pushed to a repository on GitHub. Once a pull request is opened, you can discuss and review the potential changes with collaborators and add follow-up commits before the changes are merged into the repository.

So, this is smart, right? You can add your code to the Git repo without breaking any existing stuff on the master branch, and through this PR you can then ask a more experienced developer to do a CR (code review) on your PR and accept it or not.

You’re smart; you see the huge benefit of this vs. just committing something to master and potentially breaking something in production.

Now, to circle back a bit and explain how could it be that you would break something!? Well, there’s a thing called CI (Continuous Integration) and CD (Continuous Deployment), and you can read more about it, but in essence, it’s this:

Every time someone pushes a new commit to master branch, a whole set of deploy scripts are being triggered, and essentially the new code that’s pushed is going to appear in production.

What this means is that if you change that color and you commit it to master (you’ll learn in a minute how to actually do this commit action), it will ‘automagically’ appear in the production on the website.

⚠️ Of course, the premise is that the service is set like that in the first place.

So, you get the point and value of additional branches, and you create a new branch by executing the following command in your Terminal:

git checkout -b poppingTitle

The upper command is a shortcut for two commands:

git branch poppingTitle
git checkout poppingTitle

The first one creates a new branch called poppingTitle from the current branch that you’re on (in our case that’s master). The second one ‘checks it out’. Meaning, it places you on that branch.

Adding some code

Now you’re finally ready to add some code!

Open up the style.css file, change the color, and save the file. It should look like this now:

.title {
    color: #4872A2;
}

One useful command that you’ll use a lot is git status, which will you tell you the status of your git project. If you execute that in your Terminal you will see that the style.css file was modified:

On branch poppingTitle
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   style.css

no changes added to commit (use "git add" and/or "git commit -a")

As you can see, in the instructions from the output, these changes are not yet added to the commit. To do that, execute the command git add style.css. Of course, if you were to have multiple files you would not have to list each and every one of them, you would just use git add . command.

If you run the git status command again, you’ll see that we have changes that are ready to be committed:

On branch poppingTitle
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   style.css

Committing code

To commit (one could use words like save or apply here for better clarity) the added changes to your local repository, just run:

git commit -m "Changed the title color"

Of course, the commit message can be anything, but please do yourself a favor and write meaningful commit messages.

If you run the git status command again, you’ll see that we have a clean repository:

On branch poppingTitle
nothing to commit, working tree clean

Pushing to a remote repository

So, great, at this point you have the change locally in your repository, but you’d like to get this back to Github (the so-called remote repository, or origin as it’s usually referred to).

For that you have to execute the following command:

git push origin poppingTitle

You should get an output similar to this:

Counting objects: 3, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 299 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To github.com:Hitman666/MrSupervisorWebsite.git
 * [new branch]      poppingTitle -> poppingTitle

Doing a PR

If you check Github now, you’ll see that we’ve pushed this new branch:

Now, click on the Compare & pull request button.

Here you can add some comment summarizing your pull request:

Finally, click the Create pull request button.

And now (if we’re playing by the book) someone else from your team would come and review your code. At this point, you could let someone know about this via your Project Management Tool (JIRA, Trello, Leankit, Kanbanflow, etc…) by putting this task/card in the appropriate column.

Your PR is accepted

When the pull request is approved (you get a notification from Github when someone approves or rejects it), you as the author go back to Github and just click on the Merge pull request button, and that’s it:

Finally, click the Delete branch button as it’s a good practice to not keep the merged branches in the remote repo:

You get to live another day

Your merge to master goes well, it’s auto-deployed, you check the website and all is cool. You update the ticket in your ticketing system by placing it in the ‘Deployed’ column (or some similarly named column).

It’s 17:12, you close your laptop and cheerfully go home to learn more about this awesome Git thingy. ?

Learn #Git fast as if your #job depends on it https://t.co/or1bTBS4Vt

— Nikola Brežnjak (@HitmanHR) December 17, 2017

Ionic3

How to polish our existing Ionic3 calculator application

This is the third post in a series of posts which will teach you how to take advantage of your web development knowledge in building hybrid applications for iOS and Android. The first post in this series was all about How to get started with Ionic framework 3 on Windows and Mac, the second one was about How to create a calculator application with Ionic framework 3 by using Ionic Creator for UI.

In this post you’ll learn:

  • How to polish your existing calculator application
  • How to create icons and splash screen images automatically
  • How to implement Google AdMob ads
  • How to share your application with other users without going through the app stores
  • How to test your application on the real physical devices and emulators

So, if you’ve been following these series of posts then you probably already have the Calculator application ready and running on your machine in the web browser by using the ionic lab command.

If you just dropped by, or you would like to start from scratch, then you can clone the finished version of the 2nd tutorial from Github. After cloning, you have to run npm install to install all the dependencies. After this, the command ionic lab should run the application locally on your computer, and you should see it open up in your default browser. Of course, all this should work if you have Ionic properly installed. If not, please check out the instructions in the first tutorial.

At this point, we have a working simple calculator application that lacks some sanitization checks and design improvements. We’ll fix that in this section.

Sanitization check

In our current application, we don’t have a security measure against the possible malformed input. For example, one could enter two plus (+) signs one after another, which would consequently produce an error.

So, to handle this, we’re going to wrap our evaluation line of code in the try/catch block, and we’re going to show an alert to the user using the AlertController dialog (injected as a dependency in the CalculatorPage class constructor). The whole contents of the calculator.ts file should look like this now:

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { AlertController } from 'ionic-angular';

@Component({
    selector: 'page-calculator',
    templateUrl: 'calculator.html'
})
export class CalculatorPage {
    constructor(public navCtrl: NavController, private alertCtrl: AlertController) { }

    result = '';

    btnClicked(btn) {
        if (btn == 'C') {
            this.result = '';
        }
        else if (btn == '=') {
            if (this.result == '') {
                return;
            }

            try {
                this.result = eval(this.result).toFixed(2);
            } catch (error) {
                this.showAlert();
                this.result = '';
            }
        }
        else {
            this.result += btn;
        }
    }

    showAlert() {
        this.alertCtrl.create({
            title: 'Malformed input',
            subTitle: 'Ooops, please try again...',
            buttons: ['Dismiss']
        }).present();
    }
}

Additionally, we’re checking to see if the result variable contains anything at the time the equals button is pressed, to avoid the undefined error that would otherwise happen. You can test this yourself by running the application clicking the equals button = and then on, for example, button 5, and you will see the text undefined5 appear in the Result area.

Also, you may have noticed that we added the toFixed(2) after our eval function call, to show the result formatted to two decimal places.

If you intentionally make an error while inputting the formula you would get this message:

Clearly, there are multiple ways you could approach this problem further to create a better user experience. One idea is to check on every button click if the current formula would execute correctly with eval and if not then immediately inform the user, or completely ignore the last clicked button. I encourage you to create your own way of the “improved UX” and share it in the comments.

Design changes

To be honest, our app currently doesn’t look quite representative. We’re going to change that to make it a bit nicer. If you ask any web designer these days, they will tell you that using pure CSS is, well, outdated. Ionic, as awesome as it is, has support for SASS out of the box so you can take advantage of the variables, nesting, mixins and all other great stuff that SASS provides.

For example, if you want to change the color you only need to change one variable, without having to trace it through all the files and change the particular color in all the places that it is used.

In the previous tutorial we added the ‘energized’ color to the colors array in the src/theme/variables.scss file. Now we’re going to do all of our changes in the pages/calculator/calculator.scss file, which, for now, looks like this:

page-calculator {

}

These changes will only apply to this page. If we’d like to add some styles that would apply to our whole app, we can do so in the src/app/app.scss file.

On the image below you can see what I came up with after changing few of the CSS rules, (and a slight addition to our HTML template that I’ll address additionally):

I’m sure that more design-inclined readers will come up with way more slick design than this and I encourage you to share your images/scss changes with the rest of us.

Now we’re going to go through the parts that were changed so that you can follow along in your example and see for yourself.

As for the changes, here is the final content of the src/pages/calculator/calculator.html file:

<ion-header>
    <ion-navbar>
        <ion-title>
            SuperSimple Calculator
        </ion-title>
    </ion-navbar>
</ion-header>

<ion-content>
    <div class="container">
        <form class="myInputRow">
            <ion-item>
                <ion-input type="text" placeholder="0" name="display" [(ngModel)]="result"></ion-input>
            </ion-item>
        </form>

        <div class="row">
            <button class="col2" ion-button color="danger" (click)="btnClicked('C')"> C </button>
            <button class="col" ion-button color="energized" (click)="btnClicked('%')"> % </button>
            <button class="col" ion-button color="energized" (click)="btnClicked('/')"> / </button>
        </div>

        <div class="row">
            <button class="col" ion-button (click)="btnClicked('7')"> 7 </button>
            <button class="col" ion-button (click)="btnClicked('8')"> 8 </button>
            <button class="col" ion-button (click)="btnClicked('9')"> 9 </button>
            <button class="col" ion-button color="energized" (click)="btnClicked('*')"> * </button>
        </div>

        <div class="row">
            <button class="col" ion-button (click)="btnClicked('4')"> 4 </button>
            <button class="col" ion-button (click)="btnClicked('5')"> 5 </button>
            <button class="col" ion-button (click)="btnClicked('6')"> 6 </button>
            <button class="col" ion-button color="energized" (click)="btnClicked('-')"> - </button>
        </div>

        <div class="row">
            <button class="col" ion-button (click)="btnClicked('1')"> 1 </button>
            <button class="col" ion-button (click)="btnClicked('2')"> 2 </button>
            <button class="col" ion-button (click)="btnClicked('3')"> 3 </button>
            <button class="col" ion-button color="energized" (click)="btnClicked('+')"> + </button>
        </div>

        <div class="row">
            <button class="col" ion-button (click)="btnClicked('0')"> 0 </button>
            <button class="col" ion-button (click)="btnClicked('.')"> . </button>
            <button class="col2" ion-button color="danger" (click)="btnClicked('=')"> = </button>
        </div>
    </div>
</ion-content>

Here is the breakdown of the changes that I did:

  • changed the title to SuperSimple Calculator from just Calculator on the ion-title tag
  • removed all the id attributes
  • removed the ion-label in the form element
  • added the myInputRow class to the form element
  • added the row class to the div elements
  • added the col class to the button elements, except + and = buttons to which I’ve added the col2 class
  • wrapped everything in a div with the container class

In the scss/ionic.app.scss file I added the definitions for these new classes:

page-calculator {
    .container {
        display: flex;
        flex-direction: column;
        height: 100%;

        .row {
            flex: 2;
            flex-direction: row;
            padding: 0px !important;
            margin: 0px !important;

            .col {
                flex: 1;
                padding: 0px !important;
                margin: 0px !important;
            }

            .col2 {
                flex: 2;
                padding: 0px !important;
                margin: 0px !important;
            }
        }

        .myInputRow {
            flex: 1 !important;

            input {
                font-size: 40px;
                text-align: right;
            }
        }

        button {
            height: auto !important;
            border-radius: 0px !important;
            font-size: 32px;
            font-weight: bold;
            border-left: 1px solid #fff;
            border-bottom: 1px solid #fff;
        }
    }
}

Flexbox is used here as a basis for making the layout which fills the whole content horizontally. You can learn more about it from this Ionic specific tutorial, or you can learn more about Flexbox in general from this tutorial.

This way we now have an interface which fills the whole available content.

How to create icons and splash screen images automatically in Ionic framework 3

The icon is an important part of your application because it represents your application’s brand, and it helps to identify quickly where the app is on your phone. In case you’re familiar with creating apps then you will remember that it is a tedious process to create a lot of different size images both for iOS and Android platforms.

Also, the same goes for the so-called splash screen that shows up every time the application starts. Although having a splash screen is not mandatory, it certainly adds up to the feeling of a complete and professional application, which one would certainly want to convey with his application.

Ionic helps tremendously with this by providing a single Ionic CLI command to generate all the needed icon and splash screen sizes for us automatically. Also, Ionic created Photoshop Splash Screen Template, which you can download for free and use as a guideline for creating an icon. However, if you create you project by using ionic start command, as we have, then you’ll already have both the icon.png and splash.png files in the resources folder, and you can just edit them.

For when you’re creating a branded product having a custom made icon is definitely a must. However, in this case, I’ll show you how to use one of the free services to search for a free icon which you’ll then use in your application (even if your application is a commercial application).

I tend to use IconFinder a lot, and here are the settings which you have to use to filter out the calculator images that are Free (PRICE) and can be used in commercial applications and that don’t even require a link back (LICENSE TYPE).

Of course, you can also choose to buy an image if you happen to find one that you like. You can additionally search by format, size, and background. The filters should look like this (use this prepared link which sets them automatically):

I’m going to use the last one in the first row. Simply click on it, and you should get to the download page that looks like this:

To download it just click on the green ‘Download PNG’ button.

Now, in the resources folder find and open the icon.png file with an image editor of your choice. I’m going to use Gimp in this example as it’s free cross-platform image editor available for all the major operating systems (Linux, OS X, Windows). You should see something like this:

Now you need to do a few steps:

  • select the whole area (Ctrl + a or Command + a) and press the DEL button on your keyboard. This should leave you with a blank white canvas.
  • drag the calculator icon on this white canvas and you should see something like this:
  • click on the scale tool
  • then click on the calculator image. You should get this popup:
  • change the values in the popup to 1024px for both Width and Height and click the Scale button.
  • click on the Alignment Tool, then click on the calculator image and then on the circled two buttons on the image below. This will align the calculator image horizontally and vertically

Now save the file by going to File->Overwrite icon.png:

Repeat the process to create the splash screen image and name it (overwrite it as) splash.png. The image I came up with looks like this:

Now that you have both icon.png and splash.png images ready, navigate with your Terminal/Command prompt to the root folder of the application and run the following command:

ionic cordova resources

You should get the following output:

✔ Collecting resource configuration and source images - done!
✔ Filtering out image resources that do not need regeneration - done!
✔ Uploading source images to prepare for transformations - done!
✔ Generating platform resources: 48 / 48 complete - done!
✔ Modifying config.xml to add new image resources - done!

If, instead, at this point, you get an error like this:

[ERROR] No platforms have been added. Please run: ionic cordova platform add

That means that you haven’t added any platforms yet to which Ionic should build. Since we’re going to build the app for both Apple Store and Android Play Store we’re going to use the following two commands:

ionic cordova platform add android
ionic cordova platform add ios

You should see an output similar to this:

ionic cordova platform add ios
> cordova platform add ios --save
✔ Running command - done!
Using cordova-fetch for cordova-ios@~4.4.0
Adding ios project...
Creating Cordova project for the iOS platform:
    Path: platforms/ios
    Package: io.ionic.starter
    Name: MyApp
iOS project created with [email protected]
Discovered plugin "cordova-plugin-console" in config.xml. Adding it to the project
Installing "cordova-plugin-console" for ios
Adding cordova-plugin-console to package.json
Saved plugin info for "cordova-plugin-console" to config.xml
Discovered plugin "cordova-plugin-device" in config.xml. Adding it to the project
Installing "cordova-plugin-device" for ios
Adding cordova-plugin-device to package.json
Saved plugin info for "cordova-plugin-device" to config.xml
Discovered plugin "cordova-plugin-splashscreen" in config.xml. Adding it to the project
Installing "cordova-plugin-splashscreen" for ios
Adding cordova-plugin-splashscreen to package.json
Saved plugin info for "cordova-plugin-splashscreen" to config.xml
Discovered plugin "cordova-plugin-statusbar" in config.xml. Adding it to the project
Installing "cordova-plugin-statusbar" for ios
Adding cordova-plugin-statusbar to package.json
Saved plugin info for "cordova-plugin-statusbar" to config.xml
Discovered plugin "cordova-plugin-whitelist" in config.xml. Adding it to the project
Installing "cordova-plugin-whitelist" for ios
Adding cordova-plugin-whitelist to package.json
Saved plugin info for "cordova-plugin-whitelist" to config.xml
Discovered plugin "ionic-plugin-keyboard" in config.xml. Adding it to the project
Installing "ionic-plugin-keyboard" for ios
Adding ionic-plugin-keyboard to package.json
Saved plugin info for "ionic-plugin-keyboard" to config.xml
--save flag or autosave detected
Saving ios@~4.4.0 into config.xml file ...
✔ Copying default image resources into ./resources/ios - done!

> cordova platform add android --save
✔ Running command - done!
Using cordova-fetch for cordova-android@~6.2.2
Adding android project...
Creating Cordova project for the Android platform:
    Path: platforms/android
    Package: io.ionic.starter
    Name: MyApp
    Activity: MainActivity
    Android target: android-25
Subproject Path: CordovaLib
Android project created with [email protected]
Installing "cordova-plugin-console" for android
Installing "cordova-plugin-device" for android
Installing "cordova-plugin-splashscreen" for android
Installing "cordova-plugin-statusbar" for android
Installing "cordova-plugin-whitelist" for android

               This plugin is only applicable for versions of cordova-android greater than 4.0. If you have a previous platform version, you do *not* need this plugin since the whitelist will be built in.

Installing "ionic-plugin-keyboard" for android
--save flag or autosave detected
Saving android@~6.2.3 into config.xml file ...
✔ Copying default image resources into ./resources/android - done!

After this, the ionic cordova resources command should work.

From the output, you can see that 48 images were created and hopefully now you realize how much time this saved. All the needed configuration regarding the icons and splash screens was generated by Ionic and placed in the config.xml file.

It’s worth noting that you will not see the icon nor the splash screen when using the browser testing or Ionic View testing (discussed in more detail in the How to use Ionic.io cloud service to share our application with other users without going through the app store section below). Instead, you will only see these once you deploy them to the actual physical device or the emulator (which we’ll cover in the How to test our application on the real physical devices and emulators section below).

⚠️ You can add an iOS platform if you’re developing on a Windows machine, and ionic cordova resources command will generate icons and splash screens for it. However, keep in mind that you will not be able to build the project for iOS on your Windows machine. Instead, you’ll need a Mac computer to do so. We’ll cover building the app in more detail in the How to test our application on the real physical devices and emulators section below.

How to implement Google AdMob ads

There are multiple ways you can earn money with your app these days and here are just a few:

  • Paid app – set a price for your app directly on the App/Play Store that users need to pay before downloading your app
  • Freemium – give the app for free but charge for in-app purchases like adding some extra features (think more gold or faster production in game apps)
  • Ad-based – show ads inside your application. Potentially offer the in-app purchase to remove the ads

Here we’re going to cover the Ad-based monetization option where I’ll show you how to add Google AdMob ads to our calculator application. There are two parts to implementing Google AdMob ads to an Ionic project, and I broke them into AdMob settings and Ionic settings.

AdMob settings

Let’s start with AdMob settings:

  1. Sign in/Sign up for AdMob at https://www.google.com/admob/
  2. Click the Apps and then ADD APP button:

  1. Since our app is not published yet we will click the No button:

  1. Fill in the app name and platform and click the ADD button:

  1. Save the App ID somewhere and proceed to create the Ad unit

  1. Select Banner Ad format:

  1. Configure the adds type, size, placement, style, name:

  1. You can read additional info on how to implement GA and AdMob, but for now, let’s just click Done:

  1. You will now see the following similar screen:

The most important thing to note here is this Ad unit ID, which in my test case is ca-app-pub-7957971173858308/5068937357. Make a note of this string as it’s the most important part of this setting. You can click on the copy to clipboard button and paste it as a comment (for now) in your app.

  1. Create as much Ad units as you may need (for each platform[iOS, Android] and ad format [Banner, Interstitial, etc.]). In my case, I just created the additional Interstitial Ad and will use them on both iOS and Android devices for this demo.

Ionic settings

Those of you familiar with Ionic 1 know that you can add any plugin to your Ionic project thanks to the project called ngCordova. For Ionic 3, there’s the same thing called Ionic Native.

Ionic Native is a TypeScript wrapper for Cordova/PhoneGap plugins that makes it easy to add any native functionality that you may need into your Ionic app. Ionic Native wraps the plugin callbacks in a Promise or an Observable, providing a common interface for all plugins and ensuring that native events trigger change detection in Angular.

Navigate to the root of the application with your Terminal/Command prompt and execute the following command to add the cordova-plugin-admobpro plugin:

ionic cordova plugin add cordova-plugin-admobpro

You should see the following output after running the command:

> cordova plugin add cordova-plugin-admobpro --save
✔ Running command - done!
Installing "cordova-plugin-admobpro" for android
Installing "cordova-plugin-extension" for android
Subproject Path: CordovaLib
Installing "cordova-plugin-admobpro" for ios
Plugin dependency "[email protected]" already fetched, using that version.
Installing "cordova-plugin-extension" for ios
Adding cordova-plugin-admobpro to package.json
Saved plugin info for "cordova-plugin-admobpro" to config.xml

Additionally, you also need to run this command:

npm install --save @ionic-native/admob-pro

which, if completed successfully, will only output something like added 1 package in 4.443s to the console.

You need to add this second command as well because this installs some files needed by TypeScript.

⚠️ At this point, depending on the version of Ionic CLI that you have you may need to add a platform by executing: ionic cordova platform add ios or ionic cordova platform add android depending on the platform you’re trying to build for. You have to execute that if the command ionic cordova platform ls shows that you don’t have any installed platforms on the current project:

→ ionic cordova platform ls
✔ cordova platform ls - done!
Installed platforms:
Available platforms: 
 android ~6.2.2
 blackberry10 ~3.8.0 (deprecated)
 browser ~4.1.0
 ios 4.4.0
 osx ~4.0.1
 webos ~3.7.0

If everything is fine with running ionic cordova platform add ios you will see an output like this:

Using cordova-fetch for cordova-ios@~4.4.0
Adding ios project...
Creating Cordova project for the iOS platform:
    Path: platforms/ios
    Package: io.ionic.starter
    Name: MyApp
iOS project created with [email protected]
Installing "cordova-plugin-admobpro" for ios
Installing "cordova-plugin-extension" for ios
Discovered plugin "cordova-plugin-console" in config.xml. Adding it to > the project
Installing "cordova-plugin-console" for ios
Adding cordova-plugin-console to package.json
Saved plugin info for "cordova-plugin-console" to config.xml
Discovered plugin "cordova-plugin-device" in config.xml. Adding it to > the project
Installing "cordova-plugin-device" for ios
Adding cordova-plugin-device to package.json
Saved plugin info for "cordova-plugin-device" to config.xml
Discovered plugin "cordova-plugin-splashscreen" in config.xml. Adding > it to the project
Installing "cordova-plugin-splashscreen" for ios
Adding cordova-plugin-splashscreen to package.json
Saved plugin info for "cordova-plugin-splashscreen" to config.xml
Discovered plugin "cordova-plugin-statusbar" in config.xml. Adding it > to the project
Installing "cordova-plugin-statusbar" for ios
Adding cordova-plugin-statusbar to package.json
Saved plugin info for "cordova-plugin-statusbar" to config.xml
Discovered plugin "cordova-plugin-whitelist" in config.xml. Adding it > to the project
Installing "cordova-plugin-whitelist" for ios
Adding cordova-plugin-whitelist to package.json
Saved plugin info for "cordova-plugin-whitelist" to config.xml
Discovered plugin "ionic-plugin-keyboard" in config.xml. Adding it to > the project
Installing "ionic-plugin-keyboard" for ios
Adding ionic-plugin-keyboard to package.json
Saved plugin info for "ionic-plugin-keyboard" to config.xml
--save flag or autosave detected
Saving ios@~4.4.0 into config.xml file ...
✔ Copying default image resources into ./resources/ios - done!

Btw, there is also a free version of the AdMob Pro plugin. But, honestly, if you really start making money with your app, this will be a minor expense. Besides, if you’re having problems giving back to the actual plugin through which you’re making money then my dear padawan you have yet much to learn… ?

Now, let’s add this plugin to our app’s NgModule. In the src/app/app.module.ts file import AdmobPro:

import { AdMobPro } from '@ionic-native/admob-pro';

and then add it to the Providers array. The whole contents of the src/app/app.module.ts file should now looks like this:

import { NgModule, ErrorHandler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { MyApp } from './app.component';
import { CalculatorPage } from '../pages/calculator/calculator';


import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { AdMobPro } from '@ionic-native/admob-pro';

@NgModule({
    declarations: [
        MyApp,
        CalculatorPage
    ],
    imports: [
        BrowserModule,
        IonicModule.forRoot(MyApp)
    ],
    bootstrap: [IonicApp],
    entryComponents: [
        MyApp,
        CalculatorPage
    ],
    providers: [
        StatusBar,
        SplashScreen,
        AdMobPro,
        { provide: ErrorHandler, useClass: IonicErrorHandler }
    ]
})
export class AppModule { }

Now I’m going to show you the final content of the src/pages/calculator/calculator.ts file and will explain what was changed step by step:

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { AlertController } from 'ionic-angular';

import { AdMobPro } from '@ionic-native/admob-pro';
import { Platform } from 'ionic-angular';

@Component({
    selector: 'page-calculator',
    templateUrl: 'calculator.html'
})
export class CalculatorPage {
    constructor(public navCtrl: NavController, private alertCtrl: AlertController, private admob: AdMobPro, private platform: Platform) {
        this.platform.ready().then(() => {
            var admobid = {
                banner: 'ca-app-pub-7957971173858308/5068937357',
                interstitial: 'ca-app-pub-7957971173858308/5667703151'
            };

            this.admob.createBanner({
                adId: admobid.banner,
                isTesting: true,
                autoShow: true,
                position: this.admob.AD_POSITION.BOTTOM_CENTER
            })

            this.admob.prepareInterstitial({
                adId: admobid.interstitial,
                isTesting: true,
                autoShow: false
            })
        });
    }

    result = '';
    counter = 1;

    showInterstitialAd() {
        if (AdMobPro) {
            this.admob.showInterstitial();
        }
    }

    btnClicked(btn) {
        if (btn == 'C') {
            this.result = '';
        }
        else if (btn == '=') {
            if (this.result == '') {
                return;
            }

            try {
                this.result = eval(this.result).toFixed(2);

                if (this.counter++ == 5) {
                    this.showInterstitialAd();
                    this.counter = 0;
                }
            } catch (error) {
                this.showAlert();
                this.result = '';
            }
        }
        else {
            this.result += btn;
        }
    }

    showAlert() {
        this.alertCtrl.create({
            title: 'Malformed input',
            subTitle: 'Ooops, please try again...',
            buttons: ['Dismiss']
        }).present();
    }
}

First, we added the imports:

import { AdMobPro } from '@ionic-native/admob-pro';
import { Platform } from 'ionic-angular';

Then, via the constructor we pulled in the Platform and AdMobPro:

constructor(public navCtrl: NavController, private alertCtrl: AlertController, private admob: AdMobPro, private platform: Platform) { }

Then we wrapped everything in the platform.ready() promise. This is the most important part of the code! If you wouldn’t do that, it could happen that your app would start up and the plugins would still not be properly set up, and you wouldn’t see the ads displayed.

But then again, sometimes you would, and this is what it would make it a nightmare to debug. This is a very common issue that I’ve seen even back from Ionic 1 when answering the questions on StackOverflow. So, you may want to keep an ?️ on the fact that you need to wrap any plugin calls inside the platform.ready() promise, as that way you’ll be sure that all of the plugins have loaded before you’ll use them.

The code that’s executed after the promise resolves sets up our admobid object with banner and interstitial properties. Then we’re calling the createBanner and prepareInterstitial functions on the injected admob object. Note that the banner is set to show automatically when the app loads (autoShow: true) and the interstitial isn’t. Also, note that we’ve set the position of the banner ad to the bottom:

this.platform.ready().then(() => {
    var admobid = {
        banner: 'ca-app-pub-7957971173858308/5068937357',
        interstitial: 'ca-app-pub-7957971173858308/5667703151'
    };

    this.admob.createBanner({
        adId: admobid.banner,
        isTesting: true,
        autoShow: true,
        position: this.admob.AD_POSITION.BOTTOM_CENTER
    })

    this.admob.prepareInterstitial({
        adId: admobid.interstitial,
        isTesting: true,
        autoShow: false
    })
});

Then we added the showshowInterstitialAdAd function, which shows the Interstitial ad:

showInterstitialAd() {
    if (AdMobPro) {
        this.admob.showInterstitial();
    }
}

Of course, at this point, change the adId variable to your admob_key which you obtained in the first part (step 9). Also, when you’ll be publishing your app, don’t forget to change the isTesting variable to false. Don’t worry, I’ll remind you when we get to that part.

? If you run into any problems with this, just ping in the comments, and I’ll do my best to help you.

One common thing that you might have to do for Android is to install some extras via the Android SDK manager. To do so open Android Studio and select Configure->SDK Manager:

Make sure you have installed the packages marked as Installed on the image below (usually, those are Google Billing Library and Google Play services):

For more information about the Android SDK manager, take a look at the section about How to test our application on the real physical devices and emulators futher in the post.

What kind of an ad should you show?

This plugin’s documentation states an interesting fact that it’s strongly recommended to use the Interstitial ad type because it brings more than 10 times profit than the banner Ad. Here’s the table from the official documentation:

Ad Format Banner Interstitial
Click Rate < 1% 3-15%
RPM (1k impressions) 0.5$ – 4$ 10-50$

Banner ad is the small add that is usually placed in the bottom of the screen, whereas Interstitial ads are full-screen ads that cover the interface of their host app. Therefore, you may rather want to opt for this kind of an ad instead for the Banner one.

It’s important to note that there’s probably no exact formula here on when to show the Interstitial Ad, but there are some best practices, and this is what Google has to say about it:

Interstitial ads work best in apps with natural transition points. The conclusion of a task within an app, like sharing an image or completing a game level, creates such a point. Because the user is expecting a break in the action, it’s easy to present an interstitial without disrupting their experience. Make sure you consider at which points in your app’s workflow you’ll display interstitials, and how the user is likely to respond.

You can learn a bit more about it here.

Anyways, as you saw in my demo code above, I’ve opted for showing the banner ad all the time in the bottom of the screen, and I’m showing the interstitial ad every 5th time the user clicks the = button. You can tweak this any way you like in your app, but please remember the points mentioned above about best practices.

How to use Ionic.io cloud service to share our application with other users without going through the app store

Having the ability to immediately get feedback on something that you’re working on, without having to install the app manually on your client’s devices or waiting for the App Store/Play Store approval is indispensable for constant feedback loop which is crucial in rapid application development. This is something that Eric Ries stresses a lot in his book The Lean Startup.

In Ionic, this is easier than you may have thought. All you have to do is create the account on https://apps.ionic.io and after that in your Terminal/Command prompt in the root of the project just type the following command:

ionic upload

This will prompt you to login with your username and password combination which you created in the previous step. After successful login, the app will be uploaded to the Ionic.io cloud service and you will be able to view the app through an app called Ionic View, which you can download for free on your smartphone from the App Store/Play Store.

The output of the above command is short:

[INFO] Running app-scripts build: 

[09:55:39]  build dev started ... 
[09:55:39]  clean started ... 
[09:55:39]  clean finished in 2 ms 
[09:55:39]  copy started ... 
[09:55:39]  transpile started ... 
[09:55:42]  transpile finished in 2.30 s 
[09:55:42]  preprocess started ... 
[09:55:42]  deeplinks started ... 
[09:55:42]  deeplinks finished in 4 ms 
[09:55:42]  preprocess finished in 5 ms 
[09:55:42]  webpack started ... 
[09:55:42]  copy finished in 2.44 s 
[09:55:47]  webpack finished in 5.10 s 
[09:55:47]  sass started ... 
[09:55:48]  sass finished in 1.39 s 
[09:55:48]  postprocess started ... 
[09:55:48]  postprocess finished in 4 ms 
[09:55:48]  lint started ... 
[09:55:48]  build dev finished in 8.84 s 
> ionic cordova prepare
> cordova prepare
✔ Running command - done!

[09:55:50]  lint finished in 1.87 s 
✔ Requesting snapshot upload - done!
✔ Uploading snapshot - done!
[OK] Uploaded snapshot 19a5c87e-92aa-4757-b2c0-1c24e7ee9657!

And, as stated in the output, to share your app with a client or a friend, without first having to go through the App Store/Play Store, go to https://apps.ionic.io and add the testers email under the Settings->Collaborators section:

User will receive an email which will guide him through the setup:

⚠️ Those of you coming from Ionic 1 and being used to the ionic share EMAIL command, you’ll get the following error message if you run it:
ionic share [email protected]

[ERROR] ionic share has been removed as of CLI 3.0.
The functionality now exists in the Ionic Dashboard: https://apps.ionic.io

If you’ll get an error like this:

[ERROR] Your project file (./ionic.config.json) does not contain ‘app_id’. Run ionic link.

Then execute the command ionic link and choose the Create new app option in the terminal. That will bring you to the web interface where you have to enter the name of your app and click the Create App button:

Once the app is created you’ll see something like this:

Now go back to your Terminal and run ionic link again and choose the name your app. You should see an output similar to this:

✔ Looking up your apps - done!
? Which app would you like to link SuperSimpleCalculator (dec5230f)
> ionic config set app_id dec5230f
[OK] app_id set to dec5230f in ./ionic.config.json!
[OK] Project linked with app dec5230f!

Now you can rerun the ionic upload command and get an output that the snapshot of your app has been uploaded.

Once your client/tester/friend gets the email for being a Collaborator, he will be guided through setting up Ionic View and viewing your app.

However, there’s one more way for your client/friend to try out the app. First, tell them to download the Ionic View app from the App Store/Play Store:

They will have to sign up (it’s free):

And once they’re logged in, they have to click the PREVIEW A SHARED APP button:

At this point, they will have to enter your App Id which you’ll have to send them (Slack, Email, Messenger, choose your ?). You can find your id in the Ionic dashboard. In my case the id is dec523f:

They have to enter this id in the form and click the LOAD APP button:

They will get the following screen with instructions on how to use the app:

You will notice that we will not see ads in this version because Ionic View does not support the AdMob plugin:

Suffice to say; you dear reader can also try this out via your own Ionic View app.

How to test our application on the real physical devices and emulators

If you run the application (to which you added AdMob) in the browser, you will see few notices in your browser’s Console output that AdMob is not defined:

Also, if you view the app in Ionic View, you will notice that no ads will show up. This is because Cordova plugins don’t work in the browser, nor in the Ionic View. Instead, they have to be tested on the real device or in an emulator (in iOS terminology the word simulator is used). Just for reference, there are some plugins that work in the Ionic View.

In this section, we’re going to show how to install the needed prerequisites and how to run our application in an simulator/emulator and on the actual physical device for both iOS and Android.

iOS settings

The main tool for developing native iOS applications is Xcode. Xcode is praise-worthily free for download, and it comes with a lot of simulators in which you can see how your app would look like on a real device.

However, if you want to build and deploy iOS applications to the App Store, you need to have a Mac computer since Xcode only works on their operating system. Yes, even if you’re using Ionic, you need to use Xcode to build the application for iOS.

There are some ways around this, like for example with using a so-called Hackintosh computer, but honestly, from my experience, this is just not worth it.

If you’re really anti-Apple ?, then you’ll be happy to know that there are services which allow you to rent a Mac in the cloud, just for the time you need to build your application. Also, Ionic offers the so-called Package service that allows you to build mobile apps for any supported platform even if you don’t have a Mac. We won’t cover that here, but you can take a look at the official Package service documentation.

To publish an app in the App Store (or test it on your own iOS device) you’ll need to purchase the Apple Developer Program license which costs US $99 per year. You’ll need to sign up at http://developer.apple.com, but don’t worry about this for now; we’ll cover all the steps in the next tutorial where I’ll guide you through the process of deploying the application for both the Apple’s App Store and Google’s Play Store.

Installing the needed tools

As we’ve mentioned before, you need to install Xcode, and you can do that by opening up the App Store application on your Mac and search for Xcode. You install it like any other application by clicking on the download button and following the NextNextNext type of installation.

After the installation is finished, open up Xcode, and navigate to Xcode -> Preferences -> Downloads tab. Here you can download the latest version of the Simulator available. In my case, at the time of this writing, the latest version is iOS 11.0 Simulator, as you can see on the image below:

Also, we need to install the ios-sim package with npm:

npm install -g ios-sim

Running our application in a simulator

Now, to test our application in a simulator all we have to do is execute the following command:

ionic cordova emulate ios

After a bunch of cryptic messages you should see a Xcode simulator open up and show your awesome application along with a splash screen and ads:

⚠️ In case you get an error like this:

** BUILD SUCCEEDED **
Error: Cannot read property 'replace' of undefined
[ERROR] An error occurred while running cordova emulate ios (exit code 1).

Then execute the following command (don’t use sudo if on Windows):

cd platforms/ios/cordova/node_modules/ && sudo npm install ios-sim@latest

After this repeat the emulate command and all should be well.

Of course, run the ionic cordova emulate ios command in the root directory of the project, not in the /platforms/ios/cordova/node_modules folder!

If you test the app by doing five calculations, then after the fifth time you press the equals button = the Interstitial ad will appear, as shown in the image below:

If you exit the simulator back to the home screen (by pressing `Command (⌘) + Shift + H) you will see the application’s icon:

When you’re testing your application in a browser with ionic lab you have that great live reload feature. You can have that too in the simulator if you run the simulator with the extra -l flag. If you also want to see the console.log output, as you would see it in the browser, then you have to add the –c flag:

ionic cordova emulate ios -lc

Since we need to have an Apple Developer account to run the application on our physical phone device, we will leave this for our next tutorial where we’ll also show how to deploy the application to the actual App Store.

⚠️ At the time of writing this you can see in the updated screenshots of the iOS 11 simulator, the app isn’t iOS 11 ready. However, anyone that will be following this tutorial anew and will have ionic and cordova npm packages up to date, will not have this problem, as the Ionic team fixed everything in time for the iPhone X release. For more information, see the official blog post. For those of you who are maybe looking to update your Ionic 1 app to look nice on iPhone X, take a look at this tutorial.

Android settings

There are no prerequisites for Android development regarding the operating systems; you can develop for Android on Mac, Linux, and Windows computers.

Android provides some tools for developers that are available for free from their website.

? To publish an app in the Play Store you’ll need to purchase the Developer Program license which costs only US $25 one-time fee. You’ll need to sign up at https://play.google.com/apps/publish/signup/, but as said before – don’t worry; we’ll cover all the steps in the next tutorial.

Installing the needed tools

Download Android Studio from https://developer.android.com/studio/index.html

The installation is a simple NextNextNext type of installation, but if you get stuck, they have great official documentation.

Next, you need to set up the so-called PATH variable to point to the Android SDK tools. You can find the path by opening up the Android Studio, select the Configure->SDK Manager option and copy the Android SDK Location:

On Mac, you can set the PATH variable like this:

export PATH=$PATH:/Users/nikola/Library/Android/sdk/tools/.

You may want to put this line to your .bash_profile (or any related that you may have like .zshrc) file. For any further details about how to set the PATH variable, you can take a look at this tutorial.

As for Windows, the command would be:

set PATH=%PATH%;C:\Dev\android-sdk\tools

⚠️ To be honest, you may still encounter some errors while trying to get this to work and I’ve answered quite a few issues on StackOverflow. So, if you get stuck at this point, just drop me a line in the comments, and I’ll do my best to help.

Once you’ve set the PATH variable correctly for your operating system, you can check if it’s OK by opening up your Terminal/Command prompt and typing:

android

You should get an output similar to this one:

android
*************************************************************************
The "android" command is deprecated.
For manual SDK, AVD, and project management, please use Android Studio.
For command-line tools, use tools/bin/sdkmanager and tools/bin/avdmanager
*************************************************************************
Invalid or unsupported command ""

Supported commands are:
android list target
android list avd
android list device
android create avd
android move avd
android delete avd
android list sdk
android update sdk

You may get a message that you don’t have java tools installed:

In that case, click the More Info... button and you’ll be directed to their website where you have to click on the DOWNLOAD button (make sure you click the JDK one): Then, on the next screen, click the Accept License Agreement and select your operating system:

After the download run the .dmg file (on Mac OS) and finish the NextNextNext type of installation. If you’re on Windows, make appropriate choices for your OS.

Finally, you need to set up the SDK packages. For those of you who are in this field a bit longer, you’ll remember that you had to do everything via the Terminal/Command prompt. Specifically, for this you had to enter:

android sdk

But, with the new version of the Android Studio, as you saw in the previous Terminal output, you don’t have to do that. The way you should open up the SDK Manager is by opening up Android Studio and then click on Configure->SDK Manager:

I recommend that you download just the most recent packages as shown on the image below. Most probably you’ll have everything installed except Google Play services and Google Play Billing Library.

Make sure that you download only the platforms that you’ll need. On the image below, only the Android 7.1.1 (Nougat) version is installed:

Now run ionic cordova build android, and open the folder platforms/android/ with Android Studio (click the Open an existing Android Studio project link/button) and you should see something like this:

Create a new emulator

You need to have an emulator on which you’ll run your Android app. To begin the process of adding it, click on the Tools->Android->AVD Manager:

Click the Create Virtual Device button:

Choose a device:

Select the system image and click the Next button. If need be, first click on the Download link.

On the final screen just click the Finish button and you should see the following screen which shows the summary of your virtual devices:

Run the app on the new emulator

Run ionic cordova emulate android and you should see an Android emulator start up and show you your app:

Run the app on the physical device

If you would like to run the application on your own Android device then you first have to run the following command to build the project:

ionic cordova build android

The last few lines of the output will be something like:

BUILD SUCCESSFUL

Total time: 1.557 secs
Built the following apk(s): 
    /Users/nikola/DEV/Ionic3/Ionic3_3rdTutorial/platforms/android/build/outputs/apk/android-debug.apk

To be able to test your application on your Android device, you have to enable the developer settings, and you can do that in few steps:

  • Open the Settings view and scroll to About Phone
  • At the bottom of the About Phone view find a Build Number and tap on it seven times to enable the developer mode.
  • After this you can go back to the Settings view, and you’ll see a new option called Developer Options

Now you have to enable the USB debugging, and you can do that in the next few steps:

  • Select Developer Options item in the Settings view
  • Scroll down until you see the USB debugging option
  • Toggle it ON
  • Now your device is set up for debugging, and when it’s connected to your computer you’ll be able to deploy the application to the device

Now connect your device and execute the following command in your Termina/Command prompt:

ionic cordova run android

Find the application in your application list and open it. You should see the ads at the bottom, and after the fifth calculation, you should see an Interstitial ad appear.

⚠️ If for some reason the ionic cordova run android command wouldn’t work for you, you can always just copy the generated .apk file manually to your connected device (for example in the Downloads folder). After that open the Downloads folder on your phone with the file manager application that you use and open the .apk file that you pasted in. This way you’ll be prompted to install the application on your phone and then you’ll be able to find it in the programs list:

Conclusion

In this tutorial, you’ve learned quite a few things. You learned how to polish the existing calculator application by improving the design and user experience. Next, you learned how to create icons and splash screen images automatically. Then, how to make money with your application by using Google AdMob ads. Also, how to share your application with other users without going through the app stores. Finally, you learned how to test your application on the real physical devices and emulators.

In the next tutorial, I’m going to show you how to prepare the application for the Apple’s App Store and Google’s Play Store and finally, how to publish the application to both Apple’s App Store and Google’s Play Store.

Until next time, keep on coding ✌️

How to polish our existing @ionicframework 3 calculator application https://t.co/uBmiwsyHps

— Nikola Brežnjak (@HitmanHR) December 15, 2017

Quick tips

How to prevent js beautify in Visual Studio Code to expand JSON objects

In this quick tip I’ll show you how simple it is to prevent js-beautify in Visual Studio Code to expand JSON object. Of course, for those who use js-beautify plugin in Visual Studio Code and don’t want to automatically expand their JSON objects that they may have (for a reason) inlined.

If you’re currently not utilizing the .jsbeautifyrc file, then first create it in the root directory of your project. After this, just put this content in it:

{
    "brace_style": "collapse-preserve-inline"
}

Restart VSC and that’s it. More information about this ‘issue’ can be found in the official bug thread on Github.

Hope this helps someone ?

How to prevent js beautify in Visual Studio Code to expand JSON objects https://t.co/mNuRjy7Xht

— Nikola Brežnjak (@HitmanHR) November 27, 2017

Page 14 of 51« First...10«13141516»203040...Last »

Recent posts

  • Discipline is also a talent
  • Play for the fun of it
  • The importance of failing
  • A fresh start
  • Perseverance

Categories

  • Android (3)
  • Books (114)
    • Programming (22)
  • CodeProject (35)
  • Daily Thoughts (77)
  • Go (3)
  • iOS (5)
  • JavaScript (127)
    • Angular (4)
    • Angular 2 (3)
    • Ionic (61)
    • Ionic2 (2)
    • Ionic3 (8)
    • MEAN (3)
    • NodeJS (27)
    • Phaser (1)
    • React (1)
    • Three.js (1)
    • Vue.js (2)
  • Leadership (1)
  • Meetups (8)
  • Miscellaneou$ (77)
    • Breaking News (8)
    • CodeSchool (2)
    • Hacker Games (3)
    • Pluralsight (7)
    • Projects (2)
    • Sublime Text (2)
  • PHP (6)
  • Quick tips (40)
  • Servers (8)
    • Heroku (1)
    • Linux (3)
  • Stack Overflow (81)
  • Unity3D (9)
  • Windows (8)
    • C# (2)
    • WPF (3)
  • Wordpress (2)

"There's no short-term solution for a long-term result." ~ Greg Plitt

"Everything around you that you call life was made up by people that were no smarter than you." ~ S. Jobs

"Hard work beats talent when talent doesn't work hard." ~ Tim Notke

© since 2016 - Nikola Brežnjak