Tuesday, October 31, 2017
Monday, October 30, 2017
Friday, October 27, 2017
Thursday, October 26, 2017
Building With Vue.js 2 and Firebase
Introduction
Firebase is Google's mobile platform that helps you develop high-quality apps and grow your business. In this tutorial, you will make good use of one of Firebase's awesome features: the Realtime Database.
You will build a single page application to create books. This book will be saved to your Firebase database, and you will be able to retrieve and delete books you have created.
Let's get started.
Set Up Firebase
Go to Google's Firebase page to create a new account. When done with that, log in to your console. Click on the option to add a project. Enter your project details and click on the button CREATE PROJECT.
This will lead you to your console. The Firebase console helps you manage your Firebase configuration settings.
For this tutorial, you'll need to make access to your database public. From the panel on the left, select Database. Select Realtime Database from the options that show next by clicking GET STARTED. Making your database public involves editing the rules. So click RULES on the page that loads next.
Make your rules look like this.
{ "rules": { ".read": true, ".write": true } }
Click the option to PUBLISH when done.
With this rule, authentication is not required to perform read and write actions on your database. This is needful for the application you will be building in this tutorial.
Set Up a Project Using Vue CLI
Vue CLI allows you to scaffold Vue.js projects. If you do not have it on your machine, you can get it by running:
npm install -g vue-cli
This will install it globally on your machine. Here is how Vue-CLI is used.
vue init <template-name> <project-name>
To learn more about Vue-CLI, check the GitHub page.
For this project you will use webpack templates, so run the command below from your terminal.
vue init webpack vue-book
These are the installation options I used.
? Project name vue-book ? Project description A Vue.js project ? Author izuchukwu1 <chineduizuchukwu1@gmail.com> ? Vue build standalone ? Install vue-router? No ? Use ESLint to lint your code? Yes ? Pick an ESLint preset Standard ? Setup unit tests with Karma + Mocha? No ? Setup e2e tests with Nightwatch? No vue-cli · Generated "vue-book". To get started: cd vue-book npm install npm run dev Documentation can be found at http://ift.tt/28JYKuJ
Navigate to your project folder. The files and folders generated by Vue-CLI have a tree like this.
├── build │ ├── build.js │ ├── check-versions.js │ ├── dev-client.js │ ├── dev-server.js │ ├── utils.js │ ├── vue-loader.conf.js │ ├── webpack.base.conf.js │ ├── webpack.dev.conf.js │ └── webpack.prod.conf.js ├── config │ ├── dev.env.js │ ├── index.js │ └── prod.env.js ├── index.html ├── package.json ├── README.md ├── src │ ├── App.vue │ ├── assets │ │ └── logo.png │ ├── components │ │ └── HelloWorld.vue │ └── main.js └── static 6 directories, 19 files
Now run the command to install your dependencies.
npm install
When done, you can start your dev server by running:
npm run dev
Add Firebase to the Project
To bind Firebase data to Vue.js data properties, we will make use of the VueFire library. You can check more about it on GitHub.
Run the command below:
npm install firebase vuefire --save
Open up main.js to add VueFire. Make your main.js file look like what I have below.
#src/main.js // The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from './App' import VueFire from 'vuefire' Vue.use(VueFire) Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', template: '<App/>', components: { App } })
Set Up the Firebase Connection
Go to your Firebase console, and click on the Overview link on the left panel. Select the option to add Firebase to your web app. Copy the snippet that pops up in the window to a text file. The snippet contains your apiKey, authDomain, databaseURL, projectId, storageBucket, and messagingSenderId. You need these details to be able to access your Firebase database.
You start by importing Firebase from the core Firebase library. A Firebase instance is created using the initializeApp method. The snippet you copied has to be passed to this method as an object. This has to be done in the script section of your App.vue, like this.
#src/App.vue import Firebase from 'firebase' let config = { apiKey: "...", authDomain: "...", databaseURL: "...", storageBucket: "...", messagingSenderId: "..." }; let app = Firebase.initializeApp(config) let db = app.database() let booksRef = db.ref('books')
After creating the Firebase instance, the database reference is obtained by using app.database()
.
Book Listing
Since VueFire makes it easy to bind Vue.js data properties to Firebase, implementing the books listing feature requires you to add this.
firebase: { books: booksRef },
You add that below:
name: 'app',
Now you have access to the book items from your database. The template will look like this.
<div class="panel-body"> <table class="table table-striped"> <thead> <tr> <th>Title</th> <th>Author</th> </tr> </thead> <tbody> <tr v-for="book in books"> <td><a v-bind:href="book.url"></a></td> <td></td> </tr> </tbody> </table> </div>
The v-for
directive is used to iterate through the available books. Each book will be outputted in a new table row.
Adding a New Book
To put in place the addition of new books, you need to first define the data model that will be used.
data () { return { newBook: { title: '', author: '', url: 'http://', isbn: '' } } }
Next, set up the template to look like this.
<div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title">Add New Books</h3> </div> <div class="panel-body"> <form id="form" class="form-inline" v-on:submit.prevent="addBook"> <div class="form-group"> <label for="bookTitle">Title:</label> <input type="text" id="bookTitle" class="form-control" v-model="newBook.title"> </div> <div class="form-group"> <label for="bookAuthor">Author:</label> <input type="text" id="bookAuthor" class="form-control" v-model="newBook.author"> </div> <div class="form-group"> <label for="bookUrl">Url:</label> <input type="text" id="bookUrl" class="form-control" v-model="newBook.url"> </div> <input type="submit" class="btn btn-primary" value="Add Book"> </form> </div> </div>
The v-model
directive is used to bind the newBook
properties to the corresponding input.
The v-on
directive will lead us to create an event handler method that gets called whenever a new book is to be created. Here is what the event handler should look like.
methods: { addBook: function() { booksRef.push(this.newBook) this.newBook.title = '', this.newBook.author = '', this.newBook.url = 'http://', this.newBook.isbn = '' }, },
The addBook
method helps insert new book objects into the Firebase database. The data is also synced across all clients.
Deleting Books
Let's add the ability to delete books. Add another column to the book listing.
<td> <span class="glyphicon glyphicon-trash" aria-hidden="true" v-on:click="removeBook(book)"></span> </td>
Let's put in place a method that gets called each time the button is clicked. The method is passed the book you intend to delete, which is actually the key to the book, as you will see soon. The remove()
is called on the returned book to delete it from the database.
Here is what the method looks like.
removeBook: function (book) { booksRef.child(book['.key']).remove() }
With that, you are done with App.vue. Putting everything together, here is how your App.vue file should look.
<template> <div id="app" class="container"> <div class="page-header"> <h1>Vue Book</h1> </div> <div class="panel panel-default"> <div class="panel-heading"> <h3>Add Book</h3> </div> <div class="panel-body"> <form id="form" class="form-inline" v-on:submit.prevent="addBook"> <div class="form-group"> <label for="bookTitle">Title:</label> <input type="text" id="bookTitle" class="form-control" v-model="newBook.title"> </div> <div class="form-group"> <label for="bookAuthor">Author:</label> <input type="text" id="bookAuthor" class="form-control" v-model="newBook.author"> </div> <div class="form-group"> <label for="bookUrl">URL:</label> <input type="text" id="bookUrl" class="form-control" v-model="newBook.url"> </div> <div class="form-group"> <label for="bookIsbn">ISBN:</label> <input type="text" id="bookIsbn" class="form-control" v-model="newBook.isbn"> </div> <input type="submit" class="btn btn-primary" value="Add Book"> </form> </div> </div> <div class="panel panel-default"> <div class="panel-heading"> <h3>Books Lists</h3> </div> <div class="panel-body"> <table class="table table-stripped"> <thead> <tr> <th>Title</th> <th>Author</th> <th></th> </tr> </thead> <tbody> <tr v-for="book in books"> <td> <a v-bind:href="book.url"></a> </td> <td> </td> <td> <span class="glyphicon glyphicon-trash" aria-hidden="true" v-on:click="removeBook(book)"></span> </td> </tr> </tbody> </table> </div> </div> </div> </template> <script> import Firebase from 'firebase' let config = { apiKey: "...", authDomain: "...", databaseURL: "...", storageBucket: "...", messagingSenderId: "..." } let app = Firebase.initializeApp(config) let db = app.database() let booksRef = db.ref('books') export default { name: 'app', firebase: { books: booksRef }, data () { return { newBook: { title: '', author: '', url: 'http://', isbn: '' } } }, methods: { addBook: function() { booksRef.push(this.newBook) this.newBook.title = '', this.newBook.author = '', this.newBook.url = 'http://', this.newBook.isbn = '' }, removeBook: function(book) { booksRef.child(book['.key']).remove() } } } </script> <style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; color: #2c3e50; margin-top: 60px; } </style>
In the template, I added some Bootstrap classes. For these to work, open your index.html file and make it look like this.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>vue-book</title> <link rel="stylesheet" href="http://ift.tt/2apRjw3" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <link rel="stylesheet" href="http://ift.tt/2hbdoRr" /> </head> <body> <div id="app"></div> <!-- built files will be auto injected --> <script src="http://ift.tt/2nfnDrE" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script> <script src="http://ift.tt/2aHTozy" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> </body> </html>
Conclusion
JavaScript has become extremely popular and is now capable of building mature applications (as we've seen above). If you’re looking for additional resources to study or to use in your work, check out what we have available in the Envato Market.
In this tutorial, you learned about Firebase. You were able to connect Vue.js and Firebase using VueFire. Your application can make read and write requests to your Firebase database.
You can go further by adding more features like categories and description.
TypeScript for Beginners, Part 2: Basic Data Types
After reading the introductory TypeScript tutorial, you should now be able to write your own TypeScript code in an IDE that supports it and then compile it to JavaScript. In this tutorial, you will learn about different kinds of data types available in TypeScript.
JavaScript has seven different data types: Null, Undefined, Boolean, Number, String, Symbol (introduced in ES6), and Object. TypeScript defines a few more types, and all of them will be covered in detail in this tutorial.
The Null Data Type
Just as in JavaScript, the null
data type in TypeScript can have only one valid value: null
. A null variable cannot contain other data types like number and string. Setting a variable to null will erase its content if it had any.
Remember that when the strictNullChecks
flag is set to true
in tsconfig.json, only the value null is assignable to variables with null type. This flag is turned off by default, which means that you can also assign the null value to variables with other types like number
or void
.
// With strictNullChecks set to true let a: null = null; // Ok let b: undefined = null; // Error let c: number = null; // Error let d: void = null; // Error // With strictNullChecks set to false let a: null = null; // Ok let b: undefined = null; // Ok let c: number = null; // Ok let d: void = null; // Ok
The Undefined Data Type
Any variable whose value you have not specified is set to undefined
. However, you can also explicitly set the type of a variable to undefined, as in the following example.
Keep in mind that a variable with type
set to undefined
can only have undefined as its value. If the strictNullChecks
option is set to false
, you will also be able to assign undefined
to variables with number and string types, etc.
// With strictNullChecks set to true let a: undefined = undefined; // Ok let b: undefined = null; // Error let c: number = undefined; // Error let d: void = undefined; // Ok // With strictNullChecks set to false let a: undefined = undefined; // Ok let b: undefined = null; // Ok let c: number = undefined; // Ok let d: void = undefined; // Ok
The Void Data Type
The void data type is used to signify the lack of a type
for a variable. Setting variables to have a void
type may not be very useful, but you can set the return type of functions that don't return anything to void
. When used with variables, the type void
can only have two valid values: null
and undefined
.
// With strictNullChecks set to true let a: void = undefined; // Ok let b: void = null; // Error let c: void = 3; // Error let d: void = "apple"; // Error // With strictNullChecks set to false let a: void = undefined; // Ok let b: void = null; // Ok let c: void = 3; // Error let d: void = "apple"; // Error
The Boolean Data Type
Unlike the number
and string
data types, boolean
only has two valid values. You can only set its value to either true
or false
. These values are used a lot in control structures where one piece of code is executed if a condition is true
and another piece of code is executed if a condition is false
.
Here is a very basic example of declaring Boolean variables:
let a: boolean = true; let b: boolean = false; let c: boolean = 23; // Error let d: boolean = "blue"; // Error
The Number Data Type
The number
data type is used to represent both integers and floating-point values in JavaScript as well as TypeScript. However, you should remember that all numbers are internally represented as floating-point values. Numbers can also be specified as Hexadecimal, Octal or Binary literals. Keep in mind that Octal and Binary representations were introduced in ES6, and this can result in different JavaScript code output based on the version you are targeting.
There are also three additional special symbolic values that fall under the number
type: +Infinity
, -Infinity
, and NaN
. Here are a few examples of using the number
type.
// With strictNullChecks set to true let a: number = undefined; // Error let b: number = null; // Error let c: number = 3; let d: number = 0b111001; // Binary let e: number = 0o436; // Octal let f: number = 0xadf0d; // Hexadecimal let g: number = "cat"; // Error // With strictNullChecks set to false let a: number = undefined; // Ok let b: number = null; // Ok let c: number = 3; let d: number = 0b111001; // Binary let e: number = 0o436; // Octal let f: number = 0xadf0d; // Hexadecimal let g: number = "cat"; // Error
When the target version is set to ES6, the above code will compile to the following JavaScript:
let a = undefined; let b = null; let c = 3; let d = 0b111001; let e = 0o436; let f = 0xadf0d; let g = "cat";
You should note that the JavaScript variables are still declared using let
, which was introduced in ES6. You also don't see any error messages related to the type
of different variables because the JavaScript code has no knowledge of the types we used in the TypeScript code.
If the target version is set to ES5, the TypeScript code we wrote earlier will compile to following JavaScript:
var a = undefined; var b = null; var c = 3; var d = 57; var e = 286; var f = 0xadf0d; var g = "cat";
As you can see, this time all the occurrences of the let
keyword have been changed to var
. Also note that the octal and binary numbers have been changed to their decimal forms.
The String Data Type
The string data type is used to store textual information. Both JavaScript and TypeScript use double quotes (") as well as single quotes (') to surround your textual information as a string. A string can contain zero or more characters enclosed within quotes.
// With strictNullChecks set to true let a: string = undefined; // Error let b: string = null; // Error let c: string = ""; let d: string = "y"; let e: string = "building"; let f: string = 3; // Error let g: string = "3"; // With strictNullChecks set to false let a: string = undefined; // Ok let b: string = null; // Ok let c: string = ""; let d: string = "y"; let e: string = "building"; let f: string = 3; // Error let g: string = "3";
TypeScript also supports template strings or template literals. These template literals allow you to embed expressions in a string. Template literals are enclosed by the back-tick character (`
) instead of double quotes and single quotes that enclose regular strings. They were introduced in ES6. This means that you will get different JavaScript output based on the version that you are targeting. Here is an example of using template literals in TypeScript:
let e: string = "building"; let f: number = 300; let sentence: string = `The ${e} in front of my office is ${f} feet tall.`;
Upon compilation, you will get the following JavaScript:
// Output in ES5 var e = "building"; var f = 300; var sentence = "The " + e + " in front of my office is " + f + " feet tall."; // Output in ES6 let e = "building"; let f = 300; let sentence = `The ${e} in front of my office is ${f} feet tall.`;
As you can see, the template literal was changed to a regular string in ES5. This example shows how TypeScript makes it possible for you to use all the latest JavaScript features without worrying about compatibility.
The Array and Tuple Data Types
You can define array types in two different ways in JavaScript. In the first method, you specify the type of array elements followed by []
which denotes an array of that type. Another method is to use the generic array type Array<elemType>
. The following example shows how to create arrays with both these methods. Specifying null
or undefined
as one of the elements will produce errors when the strictNullChecks
flag is true
.
// With strictNullChecks set to false let a: number[] = [1, 12, 93, 5]; let b: string[] = ["a", "apricot", "mango"]; let c: number[] = [1, "apple", "potato"]; // Error let d: Array<number> = [null, undefined, 10, 15]; let e: Array<string> = ["pie", null, ""]; // With strictNullChecks set to true let a: number[] = [1, 12, 93, 5]; let b: string[] = ["a", "apricot", "mango"]; let c: number[] = [1, "apple", "potato"]; // Error let d: Array<number> = [null, undefined, 10, 15]; // Error let e: Array<string> = ["pie", null, ""]; // Error
The tuple data type allows you to create an array where the type of a fixed number of elements is known in advance. The type of the rest of the elements can only be one of the types that you have already specified for the tuple. Here is an example that will make it clearer:
let a: [number, string] = [11, "monday"]; let b: [number, string] = ["monday", 11]; // Error let c: [number, string] = ["a", "monkey"]; // Error let d: [number, string] = [105, "owl", 129, 45, "cat"]; let e: [number, string] = [13, "bat", "spiderman", 2]; e[13] = "elephant"; e[15] = false; // Error
For all the tuples in our example, we have set the type
of the first element to a number
and the type
of the second element to a string
. Since we have only specified a type
for the first two elements, the rest of them can be either a string or a number. Creating tuples b
and c
results in an error because we tried to use a string as a value for the first element when we had mentioned that the first element would be a number.
Similarly, we can't set the value of a tuple element to false
after specifying that it will only contain strings and numbers. That's why the last line results in an error.
The Enum Data Type
The enum
data type is present in many programming languages like C and Java. It has been missing from JavaScript, but TypeScript allows you to create and work with enums. If you don't know what enums
are, they allow you to create a collection of related values using memorable names.
enum Animals {cat, lion, dog, cow, monkey} let c: Animals = Animals.cat; console.log(Animals[3]); // cow console.log(Animals.monkey); // 4
By default, the numbering of enums starts at 0, but you can also set a different value for the first or any other members manually. This will change the value of all the members following them by increasing their value by 1. You can also set all the values manually in an enum
.
enum Animals {cat = 1, lion, dog = 11, cow, monkey} let c: Animals = Animals.cat; console.log(Animals[3]); // undefined console.log(Animals.monkey); // 13
Unlike the previous example, the value of Animals[3]
is undefined
this time. This is because the value 3 would have been assigned to dog, but we explicitly set its value to 11. The value for cow stays at 12 and not 3 because the value is supposed to be one greater than the value of the last member.
The Any and Never Types
Let's say you are writing a program where the value of a variable is determined by the users or the code written in a third-party library. In this case, you won't be able to set the type of that variable correctly. The variable could be of any type like a string, number, or boolean. This problem can be solved by using the any
type. This is also useful when you are creating arrays with elements of mixed types.
let a: any = "apple"; let b: any = 14; let c: any = false; let d: any[] = ["door", "kitchen", 13, false, null]; b = "people";
In the above code, we were able to assign a number to b
and then change its value to a string without getting any error because the type any
can accept all types of values.
The never
type is used to represent values that are never supposed to occur. For example, you can assign never
as the return type of a function that never returns. This can happen when a function always throws an error or when it is stuck in an infinite loop.
let a: never; // Ok let b: never = false; // Error let c: never = null; // Error let d: never = "monday"; // Error function stuck(): never { while (true) { } }
Final Thoughts
This tutorial introduced you to all the types that are available in TypeScript. We learned how assigning a different type of value to a variable will show errors in TypeScript. This checking can help you avoid a lot of errors when writing large programs. We also learned how to target different versions of JavaScript.
If you’re looking for additional resources to study or to use in your work, check out what we have available in the Envato marketplace.
In the next tutorial, you will learn about interfaces in TypeScript. If you have any questions related to this tutorial, let me know in the comments.
Mobile Development Tools
Traditional desktop app development is dominated by large-scale software companies with huge workforces, sometimes scattered around the globe. However, the mobile app development industry is quite different. Its ecosystem has created a new breed of small-scale and highly efficient developers. Its dominant players are powered by innovation and efficiency, rather than by the number of heads on the payroll. Even an individual developer can produce a killer app that has millions of downloads in the app stores.
So how do these individuals or small teams of developers achieve such great results, without a large human workforce? It's because the mobile app ecosystem has a broad range of software tools that simplify the workflow and improve efficiency. There are tools that even guarantee certain types of functionality, up to certain standards.
In this post, you'll be exposed to various mobile development tools that enable you to make your app unique and stand out from the rest. With these tools, you can achieve success even without a large team of programmers.
1. General-Purpose Tools
These include Software Development Kits (SDKs) that make it possible to develop for a specific platform such as Android, iOS, and Windows. In addition, Integrated Development Environments (IDEs) that help streamline the workflow also fall into this category.
1.1 Native Platform SDKs
An SDK usually consists of compiled core sources, supporting software libraries and various other components such as documentation, sample apps, etc., that are designed to facilitate app development. Platform SDKs can be defined as the immediately usable form of the platform's source code.
Android SDK & NDK
You can download Android SDK from the Android Developer website's Downloads section, free of charge. You just need to download the SDK command-line tools package that matches your OS and use the SDK Manager included there to download the other packages. Then, you can use this SDK to develop apps with or without the help of an Integrated Development Environment (IDE).
Unlike the SDK, which facilitates Java-based development, Android Native Development Kit (NDK) enables developers to implement their apps in the C or C++ languages. This comes in handy, especially when external libraries written in C or C++ are used within your app. Also, this ensures the fastest performance and maximum utilization of the device's hardware features. Therefore, if performance is the main criterion, you'd be better off using Android NDK.
You can find a large collection of Android tutorials on Envato Tuts+ that can help you get up and running in a flash.
iOS SDK
Similar to the Android SDK, you can download the iOS SDK, free of charge, once you register for an Apple Developer Center Account. However, you can't run the apps developed using that on an Apple device, unless you join the paid iOS Developer Program. However, you can still run your app on an emulator and test if it works correctly.
When you are ready to run your app on a real device, you can join the Developer Program and eventually publish the app on the App Store. If you need more help on iOS development, check out our iOS From Scratch series.
Windows 10 SDK
Microsoft has unified the app development process for different devices including PC, Mobile, Xbox One, and HoloLens, with its Universal Windows Platform (UWP). You can download the Windows 10 SDK (also supports UWP) from Microsoft Windows Dev Center and use it for developing mobile apps with other tools, such as mobile emulators, to test your app.
When used together with UWP, the Windows 10 SDK allows you to port your existing Android, iOS and older Windows apps to the latest Windows 10 platform. This is really great because it enables developers to expand their reach and target the broadest possible range of devices. Windows Phone SDK tutorials on Envato Tuts+ could be a great place to look for expert help.
1.2 Complete IDEs
IDEs help streamline your workflow and get the maximum out of the SDKs. They also save developers a great deal of time with clever features such as code completion, interactive debugging, and automated building.
Android Studio
Android Studio is the IDE officially supported by Google for building Android apps. It's an all-in-one IDE featuring intelligent code completion, fast and realistic device emulation, and robust build systems. You can share the same code base across many devices, ranging from wearables, through phones, to smart TVs.
The latest Android SDK is also bundled with Android Studio so that developers can start coding from day one. The older versions of the SDK can be downloaded via the SDK Manager included in the IDE. It's also full of sample apps, code templates, and project wizards that make life really easy for developers.
Xcode
If you are an iOS developer, you must get yourself familiar with Xcode, which includes the iOS SDK and a number of other useful tools. Similar to Android Studio, it's a productive environment for building apps for Mac, iPhone, iPad, and Apple Watch. Xcode's features include a powerful source-code editor, built-in interface builder, advanced device emulator, and complete, up-to-date documentation.
Anyway, you don't have much choice, because it's mandatory to use the Xcode IDE if you plan to publish your iOS apps to Apple's App Store. Xcode handles all the app signing and developer-key management, plus the verification process for app submission, so that developers can focus more on their core business rather than worrying about their apps' administrative overheads.
Visual Studio
Visual Studio is the IDE of choice for Windows developers. It's from Microsoft, and therefore, you'll get the full support that you'd expect from the official vendor. With Visual Studio, you can use the same code base for desktop apps, mobile apps, and other Microsoft device-based apps.
Naturally, the Windows 10 SDK is bundled with the IDE, as in the case of Android Studio and Xcode. Visual Studio makes it easy for developers to find their way around a large code base with features such as Peek To Definition, improved GoTo, and more. Depending on your capacity and budget, you can select from three versions, namely Community, Professional, and Enterprise. Also, Visual Studio comes with two OS flavors, Windows and Mac. So it doesn't matter whether you own a PC or a Mac; you can use Visual Studio to code your next mobile app.
Not only that, but Visual Studio Code is a great IDE for coding cross-platform apps in JavaScript—for React Native, for example. Visual Studio Code is especially good for React Native development with the React Native tools extension. With that, you can run React Native commands from the GUI, view smart code completion prompts for React Native APIs, and even debug your apps from within the editor.
2. Special-Purpose Tools
These are the tools that enable your app to have a competitive advantage. That's really what it takes to build a killer app or a niche market. Here, you'll read about the most popular ones.
2.1 Game Engines
Mobile games are the most popular app category, in terms of download volume, on almost all the app stores. So, if you're planning to develop a mobile game, why not try one of these game engines?
Unity
Unity is a popular game development tool that can be used for creating a compelling user experience. Its features include an all-in-one editor, 2D and 3D workflow support, realistic physics engine, built-in UI creation components, and a lot more. Based on the size of your game development studio, you can choose a suitable version from Personal, Plus, or Pro.
Unity allows you to maintain a single code base and publish to multiple platforms such as Android, iOS, and Windows Phone. Unity is essentially packed with tons of features such as 360 panoramic video support, cinematic VR, and photo- or video-based asset creation. It even has its own mobile app monetization platform with Unity Ads.
Godot
Released under the MIT license, Godot is a free and open-source game creation tool that enables you to publish 2D and 3D games to all the popular mobile platforms. You can write your programs either with its default scripting language called GDScript or in the C++ language. It also has a nice visual editor with an uncluttered UI. What's really interesting is that it supports live editing on mobile devices too.
libGDX
libGDX is a game engine with multiple publishing targets including Android and iOS. It's released under the Apache 2.0 license and maintained by an active community of talented developers. It also comes with a large collection of books and tutorials, so that it's really easy for you to get started and continue.
2.2 Computer Vision SDKs
Computer vision has made remarkable progress since its early days of conception. Now it's become such a commodity that there are thousands of apps that use some form of computer vision. The availability of the built-in camera has made computer vision-based mobile apps a widespread type of software.
IBM Watson
IBM Watson is a cloud-based suite of machine-learning tools. With its Visual Recognition service, detecting and analyzing images from a smartphone app is actually quite easy. Check out some of our Watson tutorials here on Envato Tuts+. You can even use it to guess a person's age and gender from a photograph.
Google Cloud Vision
Google Cloud Vision is another cloud-based machine learning platform that makes computer vision easier. Through the Cloud Vision REST API, Google shares its deep learning AI technology with app developers everywhere. With this API, you can easily add features such as face detection, emotion detection, and optical character recognition to your Android apps. In this tutorial, you'll learn how.
OpenCV
OpenCV was one of the early adopters of computer vision technology, and today it's one of the leaders in the field. There are several other projects, some open source, and other commercial ones, based on OpenCV. It's available for Android, iOS, and Windows platforms. In Android, you'll need Android NDK to use it.
OpenCV itself is written in C++, and you'll need to brush up your C++ skills if you intend to write programs using the SDK. You'll be able to add a wide range of computer vision based features to your app using OpenCV. They include face recognition, eye tracking and gaze estimation, object recognition, and a lot more related to both still images and videos. OpenCV is released under the BSD license and is completely free to use.
Qualcomm FastCV
Released by Qualcomm Technologies Inc., FastCV is another popular computer vision based SDK. It supports features like gesture recognition, face detection, tracking, and text recognition. Although FastCV can perform reasonably well on most ARM-based processors, it's specifically designed for Qualcomm Snapdragon processors. A high-end processor is needed to fully utilize FastCV's capabilities.
2.3 Augmented Reality (AR) SDKs
AR is slowly gaining traction and becoming more of a day-to-day mobile experience, thanks to some state-of-the-art SDKs and enthusiastic developer communities. Here are some of the leaders and trendsetters in the industry.
Vuforia
Vuforia is a widely used mobile AR platform that can support a broad range of Android, iOS, and Windows smartphones and tablets. It helps you create memorable AR experiences in mobile apps, so that those apps can stand out from the rest.
Vuforia can cater to a variety of AR use cases such as print media and magazines, toys and consumer products, boxes and product packaging, and even bottles and cans. It also supports object recognition and object tracking and enables developers to create AR and VR apps by using its Mixed Reality API. You can choose from four available pricing plans depending on your requirements.
Wikitude
Another serious player in the field, Wikitude combines mobile geo-location with other standard AR features. So it must be another essential tool for any aspiring developer who wants to produce really cool mobile AR apps. The SDK comes with different options for licensing, such as a one-time fee and yearly subscription-based models.
Wikitude can currently deploy to Android and iOS mobile targets. Its community is also full of seasoned developers who are ready to share their knowledge, and hence you could take a shortcut to accelerate your development efforts to full speed.
Tango
Tango is an AR SDK developed by Google. It currently supports only two specific device models, namely Asus ZenFone AR and Lenovo Phab 2 Pro. So, if you are interested in specifically developing for any of those devices, Tango is worth considering.
ARKit
ARKit is the AR platform of Apple. You'll need to get Xcode 9, in which ARKit is included as part of the iOS 11 SDK. So, if you don't want to mess up with third-party tools and prefer to focus only on official Apple tools, ARKit is ideal for you.
Stay tuned to Envato Tuts+ for courses and tutorials about ARKit!
3. Hybrid Development Tools
Some novice developers are intimidated by the steep learning curve normally associated with native mobile development platforms. They might have to learn new programming languages and acquire platform-specific development skills, such as mastering the testing tools.
Hybrid development tools address this issue in a clever manner. They enable developers with web development skills to develop mobile apps—without needing platform-specific knowledge. There are many such platforms available, but we'll focus only on a few popular ones.
React Native
React Native is a mobile development platform released by Facebook. It allows developers to publish native mobile apps using HTML, CSS, and JavaScript. You can maintain a single code base and publish your app on the Android or iOS platforms.
It's wonderful since you don't have to install Android Studio or Xcode on your development machine. And the real fun begins when you switch to native code within the React Native code editor itself. You can mix code snippets from several native platforms in the same code editor, and that makes you really powerful and versatile.
Ionic
Ionic is another framework that lets you build mobile apps with HTML, CSS, and JavaScript. It comes out of the gate with a library of UI components and lots of handy utilities that let you code beautiful mobile apps. Ionic is built on top of the very popular Angular framework, so if you have experience with Angular, you'll find Ionic a snap.
NativeScript
NativeScript is another framework for building cross-platform mobile apps. Like Ionic and React Native, it allows you to use CSS and JavaScript to build mobile apps, but instead of HTML you code your app layouts in XML.
NativeScript can target Android, iOS, and even the Windows Universal Platform. Unlike Ionic, which uses Cordova's WebView for rendering the UI of the app, NativeScript uses the native platform's rendering engine, which means that it provides a truly native user experience.
Adobe PhoneGap
PhoneGap was one of the pioneers of HTML-based hybrid mobile development frameworks. Since being acquired by Adobe in 2011, it's just gotten better and better.
The quickest way to get started is to download the PhoneGap desktop app and dive into coding. You can emulate apps in your browser, and you can also perform live testing on connected devices, using the PhoneGap Developer Mobile App. It can serve all the three popular platforms: Android, iOS, and Windows Phone.
If you encounter a problem while writing your app, there are thousands of posts and articles on the web that will help you fix the issues. PhoneGap has a thriving community of developers who are really knowledgeable and helpful.
Xamarin
Xamarin is another hybrid mobile development tool, but it's based on C#, instead of HTML. So, if you are coming from a Microsoft development background and want to develop cross-platform mobile apps, it's an ideal choice. You'll also need Visual Studio IDE when you develop mobile apps with Xamarin.
Conclusion
The availability of the right tools and the ability to use them correctly are the key success factors of mobile app development. So, when you plan to develop your next app, give these tools a try.
-
Mobile DevelopmentMobile Development PlatformsBala Durage Sandamal Siripathi
-
Mobile DevelopmentMobile Development LanguagesBala Durage Sandamal Siripathi
Wednesday, October 25, 2017
Testing a Node.js API
Introduction
Tests are important; they provide a safeguard for your applications or APIs. As beginners, it is possible to be oblivious of the need to write tests that cover the important parts of what you are building. Yet you will meet with it as you make progress as a developer.
In a previous tutorial, you learned how to build an API with Node.js. If you have not gone through it, I suggest you do so before continuing with this. In this tutorial, you will be writing tests for an API built using Node.js and Express. At the end of this tutorial, you will know how testing works in Node.js, and you will be able to build functional and tested APIs.
Testing Tools
You will be making use of Mocha, Expect, and Supertest.
Mocha is a JavaScript test framework which makes asynchronous testing simple and fun. Mocha comes with tons of great features which can be found on the website. You will be making use of a handful of them.
Expect is an assertion library that makes it easy for you to make better assertions. You will see how that works. Supertest provides a high-level abstraction for testing HTTP. This is needful as you will be testing an API.
Enough talk, time to write some code.
Project Setup
I have a to-do list project already set up for you. The model, config file, and part of the app.js are already done for you. Go over to GitHub and clone the repository. Or you can simply do:
git clone http://ift.tt/2zEcMu6
Your package.json should look like this.
#package.json { "name": "node-todo-api", "version": "1.0.0", "description": "", "main": "server.js", "scripts": { "start": "node server/server.js", "test": "export NODE_ENV=test || SET \"NODE_ENV=test\" && mocha server/**/*.test.js", "test-watch": "nodemon --exec 'npm test'" }, "engines": { "node": "8.0.0" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "body-parser": "^1.17.2", "express": "^4.15.3", "lodash": "^4.17.4", "mongodb": "^2.2.29", "mongoose": "^4.11.1" }, "devDependencies": { "expect": "^1.20.2", "mocha": "^3.4.2", "nodemon": "^1.11.0", "supertest": "^3.0.0" } }
Now run the command to install the dependencies.
npm install
POST Request Test
For your test, create a new folder called test; this folder should be in your server directory. Now create a new file where you will write your test. Name the file server.test.js.
In this file, start by requiring the modules you installed. You also need to require your server file and your model.
#server/test/server.test.js const expect = require('expect') const request = require('supertest') const {ObjectId} = require('mongodb') const {app} = require('./../server') const {Todo} = require('./../models/todo')
You need to have a few to-dos that will be used during tests. But these to-dos will be deleted from the test database each time you run your test suite. To handle that, create two tests like so.
#server/test/server.test.js const todos = [{ _id: new ObjectId(), text: "First test todo" }, { _id: new ObjectId(), text: "Second test todo", completed: true, completedAt: 333 }] beforeEach((done) => { Todo.remove({}).then(() => { return Todo.insertMany(todos) }).then(() => done()) })
The before block cleans your Todo database, then inserts the to-dos set above. This ensures that you have a stable amount of entries in your database so your tests do not run into issues.
With that done, you can write the test for the POST request. For your POST request, you will write two tests. The first will make a request for a valid to-do and be successful. The second will make a request with an invalid body, and this should not create a new to-do.
Here is what the test should look like.
#server/test/server.test.js describe('POST /todos', () => { // 1 it('should create a new todo', (done) => { let text = 'Test todo text' // 2 request(app) // 3 .post('/todos') .send({text}) .expect(200) .expect((res) => { expect(res.body.text).toBe(text) }) .end((err, res) => { // 4 if (err) { return done(err) } Todo.find({text}).then((todos) => { // 5 expect(todos.length).toBe(1) expect(todos[0].text).toBe(text) done() }).catch((e) => done(e)) }) }) it('should not create todo with invalid body data', (done) => { // 6 request(app) // 7 .post('/todos') .send({}) .expect(400) .end((err, res) => { if (err) { return done(err) } Todo.find().then((todos) => { // 8 expect(todos.length).toBe(2) done() }).catch((e) => done(e)) }) }) })
Run the test using the command:
npm run test
It should fail. Here is what is happening:
- Describes the test.
- Creates a new to-do and saves it as a value to text.
- You make a POST request to the /todos path of your API, sending the to-do you created as the body of the request. You expect the status code for the request to be 200 and the body of the to-do to equal the value of
text
. - An error is returned if there is any, and this will cause the request to end. Thus the next block of code will not run. If no error is encountered, the flow continues.
- A request is made to the database to find the created to-do. You expect the length of the to-dos in the database to be 1, and the text of the to-do to be equal to the value of text.
- This is the test made with invalid body data.
- A POST request is made to the /todos path. This time, you are sending a request with no body. You expect to get a 400 request. No need to check the body as you are not sending any. If an error is encountered, it gets returned and the code stops running.
- If no error is encountered, a request is made to the database to check the length of the to-dos. We expect that the database will contain only 2, which are the to-dos created at the beginning.
To get this test passing, go to your server.js file and drop in the code needed for the POST request.
#server/server.js app.post('/todos', (req, res) => { let todo = new Todo({ text: req.body.text }) todo.save().then((doc) => { res.send(doc) }, (e) => { res.status(400).send(e) }) })
GET Request Test
This is simple—the test should return the length of to-dos available in the database. As you already know, the length of the todos should be 2. Why? You guessed right. At the beginning of the test suite, you created a beforeEach
block that cleans your database and inserts new to-dos each time the test suite is run.
To test that the request to get all to-dos works, here is the code for that.
#server/test/server.test.js describe('GET /todos', () => { it('should get all todos', (done) => { request(app) .get('/todos') .expect(200) .expect((res) => { expect(res.body.todos.length).toBe(2) }) .end(done) }) })
In the above, you are making a GET request to the /todos path. This time, you are not passing anything as the body of the request because it is a GET request. You expect to get a response with the status code of 200. Then you expect that the length of the to-dos will be 2.
When you run the test, you should get an error. Try getting the error to pass on your own.
I bet you got that to work. Here is the code to get the test to pass; compare it with your solution.
#server/server.js app.get('/todos', (req, res) => { Todo.find().then((todos) => { res.send({todos}) }, (e) => { res.status(400).send(e) }) })
When a GET request is made to the /todos path, you want to find every to-do in the Todo collection and return them as to-dos. Adding this to server.js causes the test to pass.
Next, you'll write three tests for the GET request made to retrieve individual to-dos. The first should retrieve and return the to-do. The second and third should return 404 error in cases where the to-do is not found.
Open up your server.test.js and create a new describe block with the first test case.
#server/server.test.js describe('GET /todos/:id', () => { it('should return todo doc', (done) => { request(app) .get(`/todos/${todos[0]._id.toHexString()}`) .expect(200) .expect((res) => { expect(res.body.todo.text).toBe(todos[0].text) }) .end(done) })
This test makes a GET request to retrieve the first to-do available in your database. You expect to get a 200 status code, and then check that the text value of the to-do is the same as the one created.
The next test looks like this.
it('should return 404 if todo is not found', (done) => { let _id = new ObjectId('5967989ee978311656e93a59') request(app) .get(`/todos/${todos/_id.toHexString()}`) .expect(404) .end(done) })
Here you are looking for a to-do using an ID that does not correspond to the ID of any to-do saved in your database. The test expects this request to return a 404 error.
The last test is like the first but a little different; here is how it looks.
it('should return 404 for non-object ids', (done) => { let hexId = '5967989ee978311656e93a5312' request(app) .get(`/todos/${todos/hexId}`) .expect(404) .end(done) }) })
Here, you create an invalid ObjectId
and try to query the database to get a to-do matching the ObjectId
created. The test expects the request to return a 404 error.
When you run the test, they should all fail. To get them to pass, add the code below to your server.js file.
#server/server.js app.get('/todos/:id', (req, res) => { let id = req.params.id // 1 if (!ObjectId.isValid(id)) { // 2 return res.status(404).send('ID is not valid') } Todo.findById(id).then((todo) => { if (!todo) { // 3 return res.status(404).send() } res.send({todo}) //4 }).catch((e) => { res.status(400).send() }) })
- Get the ID of the to-do requested from the params.
- Check to see if the ID is valid. If the ID is not valid, an error stating this is sent.
- If the ID is valid, try to find a to-do that matches that ID using the
findById
method. If no to-do is found with that ID, a 404 error is sent. - If a to-do is found, the to-do is sent. Then you catch any error that occurs and sends it.
Run the test command once more, and it should pass.
DELETE Request Test
The test for your DELETE request will be a little like what you have for your GET request.
First, you want to test that a to-do is deleted.
#server/test/server.test.js describe('DELETE /todos/:id', () => { it('should delete a todo', (done) => { let hexId = todos[0]._id.toHexString() // 1 request(app) .delete(`/todos/${hexId}`) .expect(200) .expect((res) => { expect(res.body.todo._id).toBe(hexId) }) .end((err, res) => { // 2 if (err) { return done(err) } }) Todo.findById(hexId).then((todo) => { // 3 expect(todo.hexId).toNotExist() done() }).catch((e) => done(e)) })
Here is what is happening above:
- You set the id of the to-do to a variable called
hexId
. Next, a DELETE request is made to the path of the to-do, using the ID. You expect to get a 200 response, and the to-do obtained to match the value ofhexId
. - If any error is encountered, it is returned.
- If no error, the test goes further to query your database using the ID saved as the value of
hexId
. Since a DELETE request has been previously sent, the test expects that the to-do matching that ID does not exist.
Next, you want to test that when a request is made to delete a to-do that does not exist, the response contains a 404 error. It is important to have this, as it is not possible to delete a to-do twice. Here is how the test for this should look.
#server/test/server.test.js it('should return 404 if todo is not found', (done) => { let hexId = new ObjectId().toHexString() request(app) .delete(`/todos/${todos/hexId}`) .expect(404) .end(done) })
This creates an ID for a to-do that does not exist in the database. Then a DELETE request is made to delete the to-do. The test is expected to return a 404 error as the to-do does not exist.
You want to test that a DELETE using an invalid ID returns a 404 error, like so.
#server/test/server.test.js it('should return 404 for non-object ids', (done) => { request(app) .delete('/todos/123abc') .expect(404) .end(done) }) })
To get the test to pass, open your server.js and drop this.
#server/server.js app.delete('/todos/:id', (req, res) => { let id = req.params.id if (!ObjectId.isValid(id)) { return res.status(404).send() } Todo.findByIdAndRemove(id).then((todo) => { if (!todo) { return res.status(404).send() } res.send({todo}) }).catch((e) => { res.status(400).send() }) })
Run the command to test:
npm run test
And your tests should be passing.
Conclusion
At this point, you now know how to set up a test suite when building an API using Node.js. You have used Mocha, Expect, and Supertest to test an API. One advantage of doing this is that you do not need to always fire up Postman when building your API. With your test, you can get to know what is broken.
Before we wrap up, note that JavaScript has become one of the de facto languages of working on the web. It’s not without its learning curves, and there are plenty of frameworks and libraries to keep you busy, as well. If you’re looking for additional resources to study or to use in your work, check out what we have available on Envato Market.
Using what you now know, you are good to explore the world of testing.
Tools for React Native Development
Tools, libraries, and services are an important part of every developer’s life, no matter which environment you’re developing for. And React Native is no exception. In this article, I’ll walk you through some of the best UI frameworks, libraries, components, development tools, and web services which will make you a happier and more productive React Native developer.
Text Editors and IDEs
Visual Studio Code is a text editor which has built-in IntelliSense, debugging, and Git integration capabilities. What makes it really good for React Native development is its React Native tools extension. This allows you to execute React Native commands from the command palette, add IntelliSense for React Native APIs, and debug code within the editor itself.
For more information regarding how to set up Visual Studio Code for React Native, check out this blog post: VSCode for React Native.
If you’re using Atom, you can install the Nuclide plugin. This plugin was specifically created for working with React Native, Flow, and Hack projects. It has a built-in debugger and element inspector with all the features you’re used to in Chrome Developer Tools. Flow support means that you get autocomplete, type-hinting, and code diagnostics out of the box.
If you want to explore more IDE and editor options, check out this blog post on the Top 10 Editors for React Native.
Development Tools
Development tools have a wide scope, so I’ll be grouping each tool based on its focus:
- SDK
- code quality
- testing
- debugging
SDK
When it comes to SDKs for React Native, nothing beats Expo. Expo allows you to easily prototype an app without the need for Android Studio or Xcode. It includes a set of components and libraries to help you speed up your development.
The Expo workflow consists of the following:
- Create a new project using create-react-native-app.
- Write the code in your favorite text editor.
- Run the app using the Expo client app.
There’s no need to connect your phone to your computer. Simply scan the QR code on your terminal using the Expo client app, and it will automatically run your app. If you’re using Genymotion, Expo supports that too.
The only disadvantage when using Expo is that you cannot include any custom package which uses the device’s native functionality. Expo already includes a number of commonly used native packages such as the Camera, Facebook, and Map. But if you need to use a package that they don’t already support, then you’ll have to "eject" your app. At that point your app will be as if it was created with react-native init
, and you’ll also lose the ability to run it using the Expo client app.
Code Quality
Checking the quality of your code is important, and that is why tools like ESLint exist. In a nutshell, a linting tool allows you to be more consistent with your code by checking it against a style guide. An example of such a style guide is Airbnb’s JavaScript Style Guide which specifies rules on how JavaScript code should be written. The linting tool then checks your code against those rules to ensure that they've been followed. There’s also a style guide for React projects.
If you’re using Sublime Text, here’s a good tutorial on how you can configure it so that you can have real-time feedback on the quality of your code while you’re coding: Sublime Linting for React and ES6. If you’re using another editor or IDE, be sure to look for a corresponding plugin which uses ESLint.
If you want to add static typing to your project, you can use Flow. Flow adds static-typing on top of JavaScript without you having to make any changes to your existing codebase. This is because Flow tries to infer the type whenever possible. For new projects, though, it's recommended that you explicitly specify the type to reap the full benefits of using Flow.
To get started using Flow, here’s a tutorial on how you can set up Flow for your React Native projects.
Testing
Enzyme is a testing utility for React which allows you to assert, manipulate, and traverse your component’s output. It provides methods such as shallow()
to "shallowly" render your components, find()
to traverse the rendered component, and expect()
to assert the props or the content rendered within the component.
You can follow this guide to Using enzyme to Test Components in React Native to make your React Native app testable with enzyme. If you’re new to enzyme, you can read this tutorial on Testing React Components with Enzyme and Mocha.
Debugging
Reactotron is a desktop app that allows you to debug React and React Native apps. Some of its key features include inspecting, modifying, and subscribing to the app state, tracking HTTP requests made through the app, benchmarking the app performance, and tracking errors. If you’re using Redux, you can even dispatch actions and track sagas from within Reactotron.
Boilerplates and UI Frameworks
Snowflake is a boilerplate for full-stack React Native development. It includes everything from the front-end to the back-end of the app. So if you just want a tool that can help you quickly get started then you might find Snowflake useful. You can read the notes for more information on what packages and tools are used to put it up.
Alternatively, you can use Ignite. It's a command-line tool which also includes a boilerplate, generators, style guide for UI components, API Testing Tool, and more.
React Native already comes with UI components which you can use for user interaction. The problem is that they only have the most basic styling in order for each component to be distinguished for what it is (e.g. button, checkbox). If you want to add custom styles, you have to write your own CSS code.
This is where NativeBase comes in. It allows your app to have a truly native look and feel by implementing the same design used in native Android (Material Design) and iOS (Human Interface Guidelines) apps. Out of the box, you get custom components such as Floating Action Buttons, Spinners, and best of all, form components.
Libraries and Components
React Native has a huge community behind it, so there are lots of libraries and components that you can use. We could spend all day talking about these, so to keep things brief, I’ll focus on the following areas:
- navigation
- state management
- animations
- commonly used components and libraries
Navigation
React Navigation allows you to easily implement navigation in your React Native apps through the use of its built-in navigators such as the Stack Navigator, Tab Navigator, and Drawer Navigator. That's not all, though: in addition to in-app navigation, it also includes deep linking, Redux integration, and web integration. This makes it a really robust library for implementing navigation.
State Management
MobX provides the functionality to update and manage the app state used by React. What makes it a good candidate for state management in React is its simplicity and testability. It also has a short learning curve, so things like async functions and computed values are already handled behind the scenes.
For bigger and more complex applications, Redux is still recommended. This is because MobX is very liberal, not unlike Redux, which provides strict guidelines on how the state should be managed. So it's a wiser choice for bigger projects with more people working on them.
Animations
React Native already has an animation API built into it. In fact, there’s not just one, but two APIs for working with animation: Animated API and LayoutAnimation. Those two are very powerful but can be cumbersome, especially if all you want to do is apply basic animations such as moving an object up and down or making it bounce. In such cases, components like Animatable come in handy.
Commonly Used Components and Libraries
Here’s a list of components and libraries that are commonly used in React Native projects. These are compatible with both Android and iOS devices:
- styled-components: allows you to write CSS code to style your React components.
- react-native-calendar: for showing a calendar that users can interact with.
- react-native-datepicker: for picking dates and times.
- react-native-progress: for creating progress bars and spinners.
- react-native-spinkit: a collection of loading indicators.
- Vector Icons: allows you to use icons from your favorite icon font sources such as Font Awesome and Material Icons.
- react-native-swiper: turns a collection of images or containers into swiping components.
- react-native-scrollable-tab-view: tabbed navigation that you can swipe between.
- react-native-lightbox: for viewing images in fullscreen pop-overs.
- react-native-maps: allows you to integrate Google Maps into your apps. Not all features available in the Google Maps API are available, but the functionality it provides should be enough in most cases.
- SGListView: a memory-friendly implementation of React Native’s built-in ListView component. If you need to show huge lists in your app, use this instead of ListView.
- Formik: makes dealing with forms in React Native less painful. It allows you to get values in and out of form state, validate your forms, and handle their submission.
- react-native-i18n: for implementing internationalization in your apps.
- react-native-push-notification: implements local and remote push notifications.
- InstantSearch: a collection of components for implementing search.
- react-native-fs: allows you to access the device’s native filesystem.
- react-native-camera: a camera component which allows you to take photos and videos from your app.
- react-native-video: for playing videos from your filesystem or from a URL.
- react-native-sqlite-storage: for storing and manipulating data from an SQLite database.
- react-native-store: a key-value store based on AsyncStorage.
- react-native-webrtc: for implementing WebRTC.
Web Services
You can build serverless apps and ease the deployment of your React Native apps by using web services. There are a plethora of web services out there, but I’ll focus on the following areas:
- database
- analytics
- push notifications
- code updates
- continuous integration
Database
Realm is a real-time database with a focus on mobile apps. It includes features such as two-way data sync, offline-first capabilities, and data push. The Realm Mobile Database is open-source and cross-platform, which means that you can host the Realm Object Server on your own server and then use the Realm JavaScript library for free.
Not all features are available in the developer edition, but in most use cases you should be fine with just the developer edition because it includes the core features such as the Object Database, Realtime Synchronization, and Authentication. Here's a comparison of what you get for each edition: Realm Products and Pricing.
If Realm is too much for your needs, you can always stick with the AsyncStorage API that comes with React Native.
Analytics
Fabric is an all-in-one service that allows you, among other things, to add analytics in your app. There’s Answers, which gives you real-time statistics on how your app is being used. This includes the number of active users, the session length, and retention rate. There’s also Crashlytics, which gives you powerful crash reporting capabilities. All of it happens in real time, and you can view it in Fabric’s real-time dashboard. You can use the Fabric library to easily set up Fabric for your React Native app.
If you’d rather stick with a tried and tested solution like Google Analytics, there’s also a library that allows you to do that.
Push Notifications
There’s really no competition when it comes to implementing push notifications in apps. Firebase Cloud Messaging (previously known as Google Cloud Messaging) allows you to send push notifications to both Android and iOS apps. You can use the react-native-fcm package to communicate with FCM from your app.
Code Updates
CodePush allows you to deploy code updates to mobile apps directly to the users’ devices. CodePush acts as a central repository where you can deploy changes to HTML, CSS, JavaScript, and assets such as images. The corresponding CodePush code in the app would then pull these changes. This is great for pushing bug fixes to the app without the need for uploading it in the app store and waiting for users to update the app. You can use this package to pull updates from CodePush within your React Native app.
Continuous Integration
Bitrise is a Continuous Delivery Service for mobile app development. It allows you to run your tests, build the app, and automatically push it to your users’ devices every time you deploy your code.
Bitrise integrates with a bunch of services in every step of your development workflow. For example, when you push to your release branches on GitHub, Bitrise is notified of that push through webhooks. It will then begin running the tests. Once the tests pass, the build process begins. If it’s just a "soft release" (e.g. changes to the JavaScript code) then the changes can be deployed to the users through CodePush. But if there are changes to the native code (e.g. you added a Camera plugin), then Bitrise can also build an APK or IPA file and deploy it to Google Play or iTunes Connect.
Fastlane is a collection of tools that automate the build and release process for Android and iOS apps. For iOS, it handles tasks such as running your tests, generating screenshots, code signing, and releasing the app to the app store. It also includes beta testing tools such as Pilot and Boarding. Pilot allows you to upload your app to iTunes Connect and manage your TestFlight beta testers right from the command line. Boarding creates a signup page for TestFlight beta testers.
The tools are more geared towards iOS deployment, but you can also benefit if you’re deploying Android apps. Currently, there are only two tools available for Android deployment: Supply and Screengrab.
Supply allows you to automate the uploading of assets such as the app icon, promo graphics, and screenshots of your app. It also allows you to update your existing apps on the Google Play Store.
Screengrab, on the other hand, automates the generation of screenshots for multiple devices. Each screenshot can also be localized if your app supports multiple languages.
Conclusion
That’s it! In this article, you’ve learned about some of the tools, libraries, and services that you can use when developing React Native apps. What about you? What are your go-to tools when it comes to developing apps in React Native?
And while you're here, check out some of our other posts on React Native app development!
-
Mobile App10 Best React Native App Templates of 2017Nona Blackman
-
React NativePractical Animation Examples in React NativeWernher-Bel Ancheta
-
React10 React Native Applications for You to Use, Study, and ApplyEric Dye
-
React NativeGet Started With React Native LayoutsWernher-Bel Ancheta
Tuesday, October 24, 2017
An Introduction to Elixir Applications
In my previous articles we have discussed various Elixir terms and written a hefty amount of code. What we have not discussed, however, is how to structure and organize your code so that it is easy to maintain and release.
Applications are very common for Erlang and Elixir and are used to build reusable components that behave as stand-alone units. One application may have its own supervision tree and configuration, and it can rely on other applications that are available either locally or on some remote server. All in all, working with applications is not that complex, and people who have come, say, from the world of Ruby will find many familiar concepts.
In this article you will learn what applications are, how they can be created, how to specify and install dependencies, and how to provide environment values. At the end of the article we will do some practice and create a web-based calculator.
I will be using Elixir 1.5 in this article (it was released a couple of months ago), but all the explained concepts should apply to version 1.4 as well.
Applications?
Some might argue that the term "application" is not very appropriate because in Erlang and Elixir it actually means a component, or some code that has a bunch of dependencies. The application itself can be used as a dependency as well—in Ruby world we would call it a "gem".
All in all, applications are very common in Elixir and allow you to craft reusable components while also providing easy dependency management. They consist of one or multiple modules with zero or more dependencies and are described by the application resource file. This file contains information about the application's name, version, its modules, dependencies, and some other stuff. You may create the resource file manually, but it is much easier to do so with the mix tool that will also prepare a correct folder structure for you.
So let's see how we can create a new Elixir application!
New Application
To create a new application, all you need to do is run the following command:
mix new app_name
We can also provide the --sup
flag to create an empty supervisor for us. Let's create a new application called Sample
this way:
mix new sample --sup
This command will create a sample directory for you with a handful of files and folders inside. Let me quickly guide you through them:
- config folder contains a sole file config.exs that, as you can guess, provides configuration for the application. Initially it has some useful comments, but no configuration. Note, by the way, that the configuration provided in this file is only restricted to the application itself. If you are loading the application as a dependency, its config.exs will be effectively ignored.
- lib is the primary folder of the application that contains a sample.ex file and a sample folder with an application.ex file. application.ex defines a callback module with a
start/2
function that creates an empty supervisor. - test is the folder containing automated tests for the application. We won't discuss automated tests in this article.
- mix.exs is the file that contains all the necessary information about the application. There are multiple functions here. Inside the
project
function, you provide the app's name (as an atom), version, and environment. Theapplication
function contains information about the application module callback and runtime dependencies. In our case,Sample.Application
is set as the application module callback (that can be treated as the main entry point), and it has to define astart/2
function. As already mentioned above, this function was already created for us by themix
tool. Lastly, thedeps
function lists build-time dependencies.
Dependencies
It is quite important to distinguish between runtime and build-time dependencies. Build-time dependencies are loaded by the mix
tool during the compilation and are basically compiled into your application.
They can be fetched from a service like GitHub, for example, or from the hex.pm website, an external package manager that stores thousands of components for Elixir and Erlang. Runtime dependencies are started before the application starts. They are already compiled and available for us.
There are a couple of ways to specify build-time dependencies in a mix.exs file. If you'd like to use an application from the hex.pm website, simply say:
{:dependency_name, "~> 0.0.1"}
The first argument is always an atom representing the application's name. The second one is the requirement, a version that you desire to use—it is parsed by the Version module. In this example, ~>
means that we wish to download at least version 0.0.1
or higher but less than 0.1.0
. If we say ~> 1.0
, it means we'd like to use version greater than or equal to 1.0
but less than 2.0
. There are also operators like ==
, >
, <
, >=
, and <=
available.
It is also possible to directly specify a :git
or a :path
option:
{:gettext, git: "http://ift.tt/2y1Di4v", tag: "0.1"} {:local_dependency, path: "path/to/local_dependency"}
There is also a :github
shortcut that allows us to provide only the owner's and a repo's name:
{:gettext, github: "elixir-lang/gettext"}
To download and compile all dependencies, run:
mix deps.get
This will install a Hex client if you don't have one and then check if any of the dependencies needs to be updated. For instance, you can specify Poison—a solution to parse JSON—as a dependency like this:
defp deps do [ {:poison, "~> 3.1"} ] end
Then run:
mix deps.get
You will see a similar output:
Running dependency resolution... Dependency resolution completed: poison 3.1.0 * Getting poison (Hex package) Checking package (http://ift.tt/2y2ev01) Fetched package
Poison is now compiled and available on your PC. What's more, a mix.lock file will be created automatically. This file provides the exact versions of the dependencies to use when the application is booted.
To learn more about dependencies, run the following command:
mix help deps
Behaviour Again
Applications are behaviours, just like GenServer and supervisors, which we talked about in the previous articles. As I already mentioned above, we provide a callback module inside the mix.exs file in the following way:
def application do [ mod: {Sample.Application, []} ] end
Sample.Application
is the module's name, whereas []
may contain a list of arguments to pass to the start/2
function. The start/2
function must be implemented in order for the application to boot properly.
The application.ex contains the callback module that looks like this:
defmodule Sample.Application do use Application def start(_type, _args) do children = [ ] opts = [strategy: :one_for_one, name: Sample.Supervisor] Supervisor.start_link(children, opts) end end
The start/2
function must either return {:ok, pid}
(with an optional state as the third item) or {:error, reason}
.
Another thing worth mentioning is that applications do not really require the callback module at all. It means that the application function inside the mix.exs file may become really minimalistic:
def application do [] end
Such applications are called library applications. They do not have any supervision tree but can still be used as dependencies by other applications. One example of a library application would be Poison, which we specified as a dependency in the previous section.
Starting an Application
The easiest way to start your application is to run the following command:
iex -S mix
You will see an output similar to this one:
Compiling 2 files (.ex) Generated sample app
A _build directory will be created inside the sample folder. It will contain .beam files as well as some other files and folders.
If you don't want to start an Elixir shell, another option is to run:
mix run
The problem, though, is that the application will stop as soon as the start
function finishes its job. Therefore, you may provide the --no-halt
key to keep the application running for as long as needed:
mix run --no-halt
The same can be achieved using the elixir
command:
elixir -S mix run --no-halt
Note, however, that the application will stop as soon as you close the terminal where this command was executed. This can be avoided by starting your application in a detached mode:
elixir -S mix run --no-halt --detached
Application Environment
Sometimes you may want the user of an application to set some parameter before the app is actually booted. This is useful when, for example, the user should be able to control which port a web server should listen to. Such parameters can be specified in the application environment that is a simple in-memory key-value storage.
In order to read some parameter, use the fetch_env/2
function that accepts an app and a key:
Application.fetch_env(:sample, :some_key)
If the key cannot be found, an :error
atom is returned. There are also a fetch_env!/2
function that raises an error instead and get_env/3
that may provide a default value.
To store a parameter, use put_env/4
:
Application.put_env(:sample, :key, :value)
The fourth value contains options and is not required to be set.
Lastly, to delete a key, employ the delete_env/3
function:
Application.delete_env(:sample, :key)
How do we provide a value for the environment when starting an app? Well, such parameters are set using the --erl
key in the following way:
iex --erl "-sample key value" -S mix
You can then easily fetch the value:
Application.get_env :sample, :key # => :value
What if a user forgets to specify a parameter when starting the application? Well, most likely we need to provide a default value for such cases. There are two possible places where you can do this: inside the config.exs or inside the mix.exs file.
The first option is the preferred one because config.exs is the file that is actually meant to store various configuration options. If your application has lots of environment parameters, you should definitely stick with config.exs:
use Mix.Config config :sample, key: :value
For a smaller application, however, it is quite okay to provide environment values right inside mix.exs by tweaking the application function:
def application do [ extra_applications: [:logger], mod: {Sample.Application, []}, env: [ # <==== key: :value ] ] end
Example: Creating a Web-Based CalcServer
Okay, in order to see applications in action, let's modify the example that was already discussed in my GenServer and Supervisors articles. This is a simple calculator that allows users to perform various mathematical operations and fetch the result quite easily.
What I want to do is make this calculator web-based, so that we can send POST requests to perform calculations and a GET request to grab the result.
Create a new lib/calc_server.ex file with the following contents:
defmodule Sample.CalcServer do use GenServer def start_link(initial_value) do GenServer.start_link(__MODULE__, initial_value, name: __MODULE__) end def init(initial_value) when is_number(initial_value) do {:ok, initial_value} end def init(_) do {:stop, "The value must be an integer!"} end def add(number) do GenServer.cast(__MODULE__, {:add, number}) end def result do GenServer.call(__MODULE__, :result) end def handle_call(:result, _, state) do {:reply, state, state} end def handle_cast(operation, state) do case operation do {:add, number} -> {:noreply, state + number} _ -> {:stop, "Not implemented", state} end end def terminate(_reason, _state) do IO.puts "The server terminated" end end
We will only add support for the add
operation. All other mathematical operations can be introduced in the same way, so I won't list them here to make the code more compact.
The CalcServer
utilizes GenServer
, so we get child_spec
automatically and can start it from the callback function like this:
def start(_type, _args) do children = [ {Sample.CalcServer, 0} ] opts = [strategy: :one_for_one, name: Sample.Supervisor] Supervisor.start_link(children, opts) end
0
here is the initial result. It must be a number, otherwise CalcServer
will immediately terminate.
Now the question is how do we add web support? To do that, we'll need two third-party dependencies: Plug, which will act as an abstraction library, and Cowboy, which will act as an actual web server. Of course, we need to specify these dependencies inside the mix.exs file:
defp deps do [ {:cowboy, "~> 1.1"}, {:plug, "~> 1.4"} ] end
Now we can start the Plug application under our own supervision tree. Tweak the start function like this:
def start(_type, _args) do children = [ Plug.Adapters.Cowboy.child_spec( :http, Sample.Router, [], [port: Application.fetch_env!(:sample, :port)] ), {Sample.CalcServer, 0} ] # ... end
Here we are providing child_spec
and setting Sample.Router
to respond to requests. This module will be created in a moment. What I don't like about this setup, however, is that the port number is hard-coded, which is not really convenient. I might want to tweak it when starting the application, so let's instead store it in the environment:
Plug.Adapters.Cowboy.child_spec( :http, Sample.Router, [], [port: Application.fetch_env!(:sample, :port)] )
Now provide the default port value inside the config.exs file:
config :sample, port: 8088
Great!
What about the router? Create a new lib/router.ex file with the following contents:
defmodule Sample.Router do use Plug.Router plug :match plug :dispatch end
Now we need to define a couple of routes to perform addition and fetch the result:
get "/result" do conn |> ok(to_string(Sample.CalcServer.result)) end post "/add" do fetch_number(conn) |> Sample.CalcServer.add conn |> ok end
We are using get
and post
macros to define the /result
and /add
routes. Those macros will set the conn
object for us.
ok
and fetch_number
are private functions defined in the following way:
defp fetch_number(conn) do Plug.Conn.fetch_query_params(conn).params["number"] |> String.to_integer end defp ok(conn, data \\ "OK") do send_resp conn, 200, data end
fetch_query_params/2
returns an object with all the query parameters. We are only interested in the number that the user sends to us. All parameters initially are strings, so we need to convert it to integer.
send_resp/3
sends a response to the client with the provided status code and a body. We won't perform any error-checking here, so the code will always be 200
, meaning everything is okay.
And, this is it! Now you may start the application in any of the ways listed above (for example, by typing iex -S mix
) and use the curl
tool to perform the requests:
curl http://localhost:8088/result # => 0 curl http://localhost:8088/add?number=1 -X POST # => OK curl http://localhost:8088/result # => 1
Conclusion
In this article we have discussed Elixir applications and their purpose. You have learned how to create applications, provide various types of information, and list dependencies inside the mix.exs file. You've also seen how to store the configuration inside the app's environment and learned a couple of ways to start your application. Lastly, we have seen applications in action and created a simple web-based calculator.
Don't forget that the hex.pm website lists many hundreds of third-party applications ready for use in your projects, so be sure to browse the catalog and pick the solution that suits you!
Hopefully, you found this article useful and interesting. I thank you for staying with me and until the next time.