React Native Continuous Delivery with Github Actions and Fastlane

Github Actions is the workflow automation tool with CI/CD that allows you to do some tasks, such as running test suite, deploying code and etc based on the Github Events and Types. When an event is triggered, your defined workflow will be run and help you to do some awesome jobs.

Today, I’m going to show you how to create the workflow of building your React Native apps and publishing them to App Store Connect and Play Store using Github Actions and Fastlane. By end of this post, we will be able to build and publish iOS and Android app to respective store when a new Github Release is published. Sounds exciting? Let’s get started!

Creating Workflow

First, we will have to create a workflow in .github/workflows directory. Similar to other CI/CD services, you may configure the workflow using YAML syntax. Multiple workflow files can be created in the directory and each workflow must have at least a Job.

Now, let’s create a publish.yml workflow and put a name for the workflow.

name: Publish iOS and Android App to App Store and Play Store

Setting Trigger Event

We want to trigger the workflow when a Github Release is published. Thus, we will be using the release event in Github Actions to trigger our workflow. Besides release event, Github Actions also contains many events such pull_request, push, issues that you can hook on and run the workflow. Many events such as release, pull_request may have more than one type of activities. For example, the release event will be triggered when a release is published, created, unpublished, edited and etc.

In this post, we want to trigger the workflow when the event is release and the activity type is published.

name: Publish React Native App to App Store and Play Store

+ on:
+  release:
+    type: [published]

If you want to trigger event with more than one activity type, you may add more activity type into the array. Other than web hook events, workflows can be triggered by scheduled job and external events too.

To understand more about triggering events, please read Events that trigger workflow

Creating Jobs

As mentioned above, each workflow must have at least a Job. Since we are building iOS and Android app, let’s add two jobs: release-ios and release-android in the workflow.

name: Publish React Native App to App Store and Play Store

on:
  release:
    type: [published]

+jobs:
+  release-ios:
+    name: Build and release iOS app
+    runs-on: macOS-latest
+
+  release-android:
+    name: Build and release Android app
+    runs-on: ubuntu-latest

By default, every job will be started simultaneously. If you want to start a job after another job is completed, you can use needs to specify the dependencies of a job.

jobs:
  job1:
  job2:
    needs: job1
  job3:
    needs: [job1, job2]

In this example, job2 will starts after job1 is completed and job3 will starts after job1 and job2 is completed.

For every job, we will also need to specify which OS that the job should runs on, Github Actions provides three essential OS which is windows, ubuntu and macOS. In this post, we will use macOS-latest for iOS build and ubuntu-latest for Android build

To understand more about the virtual environment, please read Virtual environment for Github Actions

Defining Steps

Now, we can start to add the steps of building and releasing the iOS and Android app. Github Actions provides several standard actions for us to perform certain tasks such as checkout the repository, installing Node, installing Ruby and etc. You can create your own actions if you want to. We will use few standard actions such as actions/checkout, actions/setup-node and actions/setup-ruby to setup the environment.

name: Publish React Native App to App Store and Play Store

on:
  release:
    type: [published]

jobs:
  release-ios:
    name: Build and release iOS app
    runs-on: macOS-latest
+    steps:
+      - uses: actions/checkout@v1
+      - uses: actions/setup-node@v1
+        with:
+          node-version: '10.x'
+      - uses: actions/setup-ruby@v1
+        with:
+          ruby-version: '2.x'
+      - name: Install Fastlane
+        run: bundle install
+      - name: Install packages
+        run: yarn install

  release-android:
    name: Build and release Android app
    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v1
+      - uses: actions/setup-node@v1
+        with:
+          node-version: '10.x'
+      - uses: actions/setup-ruby@v1
+        with:
+          ruby-version: '2.x'
+      - name: Install Fastlane
+        run: bundle install
+      - name: Install packages
+        run: yarn install

In the workflow above, we have added few steps as following:

  1. actions/checkout@v1 – Checkout the current repository.
  2. actions/setup-node@v1 – Install Node 10.x to run React Native >= 0.60
  3. actions/setup-ruby@v1 – Install Ruby 2.x for the usage of Fastlane
  4. bundle install – Install Fastlane
  5. yarn install – Install NPM packages

Now, we have all the basic setup in place and we will continue to add more steps to build iOS and Android app using Fastlane. Let’s start with building and publishing Android app.

Build and Publish Android app

There are 2 things that we need to build and publish Android app:

  1. keystore – Signing the APK. Learn more about how to create your keystore.
  2. Google Credentials – Authenticate with Play Console for publishing the app. Learn more about how to create your Google Credential.

After you have created the keystore and the Google Credential, you may want to encrypt them using command gpg --symmetric --cipher-algo AES256 path/to/your-secret.json and commit to your repository.

Now, let’s create a script to decrypt the keystore and the Google Credential so that we can use them in our workflow. Create decrypt.sh and add the following codes:

#!/bin/sh

# --batch to prevent interactive command --yes to assume "yes" for questions
gpg --quiet --batch --yes --decrypt --passphrase="$ENCRYPT_PASSWORD" \
--output ./path/to/release.keystore ./path/to/release.keystore.gpg

gpg --quiet --batch --yes --decrypt --passphrase="$ENCRYPT_PASSWORD" \
--output ./path/to/google-key.json ./path/to/google-key.json.gpg

The ENCRYPT_PASSWORD is the password that you used to encrypt your secret files and we will put it as a environment variable later. Now let’s add the remaining steps to complete the Android workflow.

name: Publish React Native App to App Store and Play Store

on:
  release:
    type: [published]

jobs:
  release-ios:
    ...

  release-android:
    name: Build and release Android app
    runs-on: ubuntu-latest
    steps:
      ...
+      - name: Decrypt keystore and Google Credential
+        run: ./path/to/decrypt.sh
+        env:
+          ENCRYPT_PASSWORD: ${{ secrets.ENCRYPT_PASSWORD }}
+      - name: Bundle and Upload to PlayStore
+        run: bundle exec fastlane build_and_release_to_play_store versionName:${{ github.event.release.tag_name }}
+        env:
+          STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
+          KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}

We are using the name of the tag as the versionName of the app. To add environment variables in Github Actions, we can add env in the steps that need the variables. If you want to add any secrets, you may add the secrets to your Github repository’s Settings and access them using ${{ secrets.YOUR_SECRET_NAME }}.

We are almost there. Let’s add the build_and_release_to_play_store action in the Fastfile.

lane :build_and_release_to_play_store do |options|
  # Bundle the app
  gradle(
    task: 'bundle',
    build_type: 'Release',
    project_dir: "android/",
    properties: {
      "versionName" => options[:versionName],
      "versionCode" => options[:versionCode],
    }
  )

  # Upload to Play Store's Internal Testing
  upload_to_play_store(
    package_name: 'com.example.app',
    track: "internal",
    json_key: "./path/to/google-key.json",
    aab: "./android/app/build/outputs/bundle/release/app.aab"
  )
end

Awesome! We have completed the Android steps and you should able to build and publish the app to Play Store.

Building and Publish iOS app

To build an iOS app, we will need to sign the IPA before upload it to App Store Connect and there is no easy way of doing it in CI/CD environment. Luckily, Fastlane provides the sync_code_signing action for us to handle the code signing easily. If you have not setup code signing before, please follow the codesigning guideline to generate your certificates and provisioning profiles.

Once you done setup the code signing, you can add the steps to the workflow

name: Publish React Native App to App Store and Play Store

on:
  release:
    type: [published]

jobs:
  release-ios:
    name: Build and release iOS app
    runs-on: macOS-latest
    steps:
      ...
      - name: Login Github User
        run: echo -e "machine github.com\n  login $PERSONAL_ACCESS_TOKEN" >> ~/.netrc
        env:
          PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
      - name: Build and Upload to TestFlight
        run: bundle exec fastlane build_and_release_to_app_store versionName:${{ github.event.release.tag_name }} versionCode:${{ github.run_number }}
        env:
          FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }}
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}

  release-android:
    ...

In Github Actions, we can only access the current repository that the workflow is running. If you want to checkout other private repositories, you may create a <a rel="noreferrer noopener" aria-label=" (opens in a new tab)" href="https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line&quot; target="_blank">personal access token</a> and login using netrc echo -e "machine github.com\n login $PERSONAL_ACCESS_TOKEN" >> ~/.netrc. We have added this step to allow us to checkout the code signing repository.

In addition, we will need to add FASTLANE_PASSWORD and MATCH_PASSWORD in the environment variables so that Fastlane able to decrypt the certificates and provisioning profiles as well as authenticate with App Store Connect.

If your Apple account has activated two-factor authentication, you may want to add FASTLANE_SESSION and FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD into the environment variables, otherwise the workflow will fail. Read Fastlane two-factor or two-steps auth to learn more.

Now let’s add build_and_release_to_app_store actions into Fastfile.

lane :buid_and_release_to_play_store do |options|
  ...
end

lane :build_and_release_to_app_store do |options|
  # Set the build number
  increment_build_number(
    build_number: options[:versionCode],
    xcodeproj: "./ios/Example.xcodeproj"
  )

  # Set the version name
  increment_version_number(
    version_number: options[:versionName],
    xcodeproj: "./ios/Example.xcodeproj"
  )

  # Create a custom keychain for code signing
  create_keychain(
    name: 'keychain',
    password: 'password',
    default_keychain: true,
    unlock: true,
    timeout: 3600,
    add_to_search_list: true
  )

  # Import the appstore code signing
  match(
    type: "appstore",
    keychain_name: 'keychain',
    keychain_password: 'password',
    readonly: true
  )

  # Building the iOS app
  gym(
    workspace: "./ios/Example.xcworkspace",
    include_bitcode: true,
    include_symbols: true,
    silent: true,
    clean: true,
    scheme: "Example",
    export_method: "app-store"
  )

  # Upload to testflight
  testflight(
    app_identifier: "com.example.app",
    username: "your_apple_username@mail.com",
    skip_submission: true,
    skip_waiting_for_build_processing: true
  )
end

Awesome! We have completed the iOS job! Now you should able to build and publish your iOS app to App Store Connect now.

Testing Your Workflow

To test your workflow, you can create a Release and go to the Actions tab in Github to view the log of your workflow.

Github Actions is here for you!

Github Actions is a very awesome tool for every developer to start with CI/CD journey. To understand more about Github Actions, please read their documentation. Have fun trying out Github Actions ūüėä !

Optimizing For MongoDB Atlas And AWS Lambda

If you want to use MongoDB for your AWS Lambda serverless app, MongoDB Atlas may be a good option for you to start with. MongoDB Atlas has been generously offering 500MB storage for free and it is very easy to setup with just a few clicks.

Recently, I have migrated my databases to MongoDB Atlas and connected them from my AWS Lambda serverless apps. While i doing some test, however, I found that the AWS Lambda functions always timeout when connecting to MongoDB Atlas. After some studies, I have applied following changes to optimize the connection of AWS Lambda functions with MongoDB Atlas.

#1 Create Shared MongoDB Client Connection

I have moved the MongoDB connection logic out of the AWS Lambda function’s handler so that the connection can be reuse between the invocations of the AWS Lambda function as long as the function is alive.

"use strict";
const MongoClient = require('mongodb').MongoClient;
const MONGODB_URI = 'YOUR_MONGODB_ATLAS_CONNECTION_STRING';

// Shared connection between invocations
let cachedDb = null;

function connectToDatabase (uri) {
  console.log('Connect to database');

  if (cachedDb) {
    console.log('Using cached database instance');
    return Promise.resolve(cachedDb);
  }

  return MongoClient.connect(uri, { useNewUrlParser: true })
    .then(client => {
      cachedDb = client.db(database);
      return cachedDb;
    });
}

module.exports.handler = (event, context, callback) => {
  connectToDatabase(MONGODB_URI)
    .then(db => {
        // Do some queries
    })
    .catch(err => {
        // Catch error
    });
};

#2 Set context.callbackWaitsForEmptyEventLoop = false

The second changes that I have made is to set the context.callbackWaitsForEmptyEventLoop to false as the first thing in the AWS Lambda function’s handler. By setting the value to false, AWS Lambda will freeze the process as soon as the callback is invoked.

By default, the callback waits until the runtime event loop is empty before freezing the process and returning the results to the caller. Setting this property to false requests that AWS Lambda freeze the process soon after the callback is invoked, even if there are events in the event loop. AWS Lambda will freeze the process, any state data, and the events in the event loop. Any remaining events in the event loop are processed when the Lambda function is next invoked, if AWS Lambda chooses to use the frozen process.

https://docs.atlas.mongodb.com/best-practices-connecting-to-aws-lambda/

As a result, the AWS Lambda function’s code will look like following:

"use strict";
const MongoClient = require('mongodb').MongoClient;
const MONGODB_URI = 'YOUR_MONGODB_ATLAS_CONNECTION_STRING';

// Shared connection between invocations
let cachedDb = null;

function connectToDatabase (uri) {
  console.log('Connect to database');

  if (cachedDb) {
    console.log('Using cached database instance');
    return Promise.resolve(cachedDb);
  }

  return MongoClient.connect(uri, { useNewUrlParser: true })
    .then(client => {
      cachedDb = client.db(database);
      return cachedDb;
    });
}

module.exports.handler = (event, context, callback) => {
  context.callbackWaitsForEmptyEventLoop = false;

  connectToDatabase(MONGODB_URI)
    .then(db => {
        // Do some queries
    })
    .catch(err => {
        // Catch error
    });
};

Once i applied the changes above, the AWS Lambda functions’ invocation duration has been reduced from 6000ms to 100ms. I’m happy with the results and I hope these changes can help you too.

Bonus: MongoDB Atlas Free Tier Limitations

Although MongoDB Atlas has a great free tier plan, but there are some limitations for production usage:

  1. If you are using AWS Lambda like me, then you may have a problem to whitelist the IP addresses to your MongoDB because AWS Lambda does not have a fixed IP address. You will need to create assign a VPC with Elastic IP to the functions which it will be charged.
  2. There are only maximum of 100 connections available in the free tier MongoDB Atlas. If your application has high traffic, you may need to consider upgrade your plan.

To understand more, you may read the MongoDB Atlas documentation

Testing React Native With Jest, react-test-renderer And react-native-testing-library

React Native is a great framework for you to write once and build mobile app with Javascript and React to both Android and iOS platform. Everything is pretty easy until your superior telling you, “We need to start practice TDD approach because there are too many bugs surfaced in production to capture the error early”. Sounds familiar? 

Okay, today we will look into how to test React Native components using Jest and react-test-renderer as well as react-native-testing-library. There are few common scenarios that you will need to test for a component and we will cover them in this post.

  • Snapshot testing
  • Event triggering
  • Async API call
  • Timer event

In this post, all the components will be using React Hooks

Testing component with Snapshot testing

Snapshot testing allow us to easily verify if the component‚Äč UI is work as expected and not changing unexpectedly. Jest will capture a snapshot of the component when the first time you run the test and verify the result against the snapshot for the subsequence test run. If the result is not same as snapshot, it will fail the tests.

Now let’s try it with HelloWorldComponent below.

import React from 'react';
import { Text } from 'react-native';

const HelloWorldComponent = () => {
  return (
    <Text>Hello World</Text>
  );
};

export default HelloWorldComponent;

And here is the test looks like

import React from 'react';
import renderer from 'react-test-renderer';

import HelloWorldComponent from '../HelloWorldComponent';

describe('HelloWorldComponent', () => {
  test('should display Hello World', () => {
    // Render a React component
    const component = renderer.create(<HelloWorldComponent />);

    // Expect the result
    expect(component.toJSON()).toMatchSnapshot();
  });
});

Your snapshot taken will be look like this.

exports[`HelloWorldComponent should display Hello World 1`] = `
<Text>
  Hello World
</Text>
`;

It is very hard to do snapshot testing right as when your components is complex, you will have a hard time to check on the snapshot (With your eyes i mean). Here is some advice from me: Iterate the component piece by piece and commit your changes to source control so that you can verify the changes for each iteration is correct.

Testing Event Trigger

Another scenario that we commonly need to test is event triggering, such as button click. Let’s add a Button in the HelloWorldComponent.

import React, { useState } from 'react';
import { Button, View, Text } from 'react-native';

const HelloWorldComponent = () => {
  const [text, setText] = useState('Hello World');

  const onButtonPress = () => {
    setText('I have pressed the button.');
  };

  return (
    <View>
      <Button testID="button" onPress={onButtonPress} title="Change text" />
      <Text>{text}</Text>
    </View>
  );
};

export default HelloWorldComponent;

We added a Button that will change the text to “I have pressed the button” after it is pressed. You will realize that we have added “testID” props for the Button, this props will be used to look up the Button and trigger the onPress() event during the test. react-test-render provide few methods for you to look up the descendent component, you are free to use any of those look up methods.

‚ÄčNow, let’s test the onPress event.

import React from 'react';
import renderer, { act } from 'react-test-renderer';

import HelloWorldComponent from '../HelloWorldComponent';

describe('HelloWorldComponent', () => {
  test('should change text after press button', () => {
    // Render a React component
    const component = renderer.create(<HelloWorldComponent />);

    // Find the button that has props.testID === 'button'
    const button = component.root.findByProps({ testID: 'button' });

    // All codes that causes state updates should wrap in act(...)
    act(() => {
      // Call the onPress props of the button
      button.props.onPress();
    });

    // Expect the result
    expect(component.toJSON()).toMatchSnapshot();
  });
});

You should see the text is displaying as “I have pressed the button.”. In this test, we use “act” to wrap the button press event trigger because all events that causes the state updates should wrap in “act”, otherwise, the changes will not reflect to the component.

Testing Asynchronous Event

Another very common scenario is that we will retrieve some data from server and display it when we enter a page. Let’s change our HelloWorldComponent to become that scenario now.

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import { Text } from 'react-native';

const HelloWorldComponent = () => {
  const [text, setText] = useState('Hello World');

  useEffect(() => {
    const getTextFromServer = async () => {
      // Get response from server
      const response = await axios.get('https://example.com');

      // Set text
      setText(response.data);
    };

    getTextFromServer();
  }, []);

  return (
    <Text>{text}</Text>
  );
};

export default HelloWorldComponent;

We have left the dependencies of “useEffect” as empty so that it’s only trigger once, which similar to “componentDidMount”. Now let’s try to write the test.

import React from 'react';
import renderer from 'react-test-renderer';
import axios from 'axios';

import HelloWorldComponent from '../HelloWorldComponent';

describe('HelloWorldComponent', () => {
  test('should display text from server', async () => {
    // Mock response from axios
    jest.spyOn(axios, 'get').mockReturnValue({ data: 'This is server response' });

    // Render a React component
    const component = renderer.create(<HelloWorldComponent />);

    // Expect the result
    expect(component.toJSON()).toMatchSnapshot();
  });
});

It’s pretty simple, isn’t it? We just need to mock the response for “axios” and the test should be complete. Let’s run the test and wait for green light! ………..Erm……let us check on the snapshot….oh no…the text is still remaining as “Hello World”. What is wrong here? Don’t worry, this is working as expected…If you are familiar with “useEffect”, you will know it will run after the component layout and painted. You can read more for the timing of effect docs. There are thorough discussion happening in this issue about how to solve this problem but today I wanted to solve this problem using utils called react-native-testing-library which highly inspired by react-testing-library. This library provided some useful function to re-render component or clear all async tasks. Let’s make some changes to our test.

import React from 'react';
import { render, flushMicrotasksQueue } from 'react-native-testing-library';
import axios from 'axios';

import HelloWorldComponent from '../HelloWorldComponent';

describe('HelloWorldComponent', () => {
  test('should display text from server', async () => {
    // Mock response from axios
    jest.spyOn(axios, 'get').mockReturnValue({ data: 'This is server response' });

    // Render a React component
    const component = render(<HelloWorldComponent />);
    
    // Flush all tasks queued
    await flushMicrotasksQueue();

    // Expect the result
    expect(component.toJSON()).toMatchSnapshot();
  });
});

There are 2 things we changed here:
1. We are no longer directly using react-test-renderer to render the component, instead we use { render } from react-native-testing-library.
2. We added “await flushMicrotasksQueue()” to flush all the queued tasks in JS.
Now, we can re-run the test and we should see the display text as “This is server response”.

Testing Timer Event

Last thing we need to look into today is testing timer event, there are also occasion that we will need to setTimeout to trigger some changes or setInterval to repeatedly trigger some polling. Let’s add some time out event in our HelloWorldComponent.

import React, { useState, useEffect } from 'react';
import { Text } from 'react-native';

const HelloWorldComponent = () => {
  const [text, setText] = useState('Hello World');

  useEffect(() => {
    const timer = setTimeout(() => {
      setText('Timer is done!');
    }, 3000);

    return () => {
      clearTimeout(timer);
    };
  }, []);

  return (
    <Text>{text}</Text>
  );
};

export default HelloWorldComponent;

Now, HelloWorldComponent will change the text to “Timer is done!” after 3 seconds, but how can we test it after 3 seconds? Are we going to wait 3 seconds in the test? Of course no! We can use “jest.useFakeTimers()” to mock the timer and run them immediately. Here is how it’s looks for the test:

import React from 'react';
import { render } from 'react-native-testing-library';

import HelloWorldComponent from '../HelloWorldComponent';

describe('HelloWorldComponent', () => {
  test('should change text after 3 seconds', async () => {
    // Fake timer
    jest.useFakeTimers();

    // Render a React component
    const component = render(<HelloWorldComponent />);

    // Tell jest to run remaining timer and not create any new timer
    jest.runOnlyPendingTimers();

    // Expect the result
    expect(component.toJSON()).toMatchSnapshot();
  });
});

All we need to do is to fake the timer and tell jest to run the timer, then we got the result we wanted!

For this post, we have looked in to how to test for component using snapshot, event triggering, async event as well as timer event using Jest, react-test-renderer or react-native-testing-library. If you would like to use react-native-testing-library to test all the scenarios, you are free to do so too.

Thank you for reading!

Shareable ESLint Config For React Native

A wise programmer once said, “You don’t write code for yourself, you write for others or your future-self”. Imagine that you are working on a project which everyone is writing in their own style and standard. Everyday, you need to spend some times to get familiar with the code before you can start coding. It’s very inefficient for everyone’s daily works. Thus, code consistency and readability is very important for every project. Most of the programming language have their own linter, and for Javascript, Eslint is one of the best.

If you take a look at this repo, awesome-eslint, and you will notice there are tons of plugin and rules for Eslint. You may wonder what is the Eslint plugins or configs that you can use for your React Native project. Today, I’m going to share with you some basic Eslint plugins that you can have for your React Native project.

Standard Eslint for React Native

Airbnb Javascript Style Guide‚Äč
One of the most adopted Javascript Eslint config because it has the most reasonable style for Javascript programming. In this config, it’s also included linting for React and ECMAScript 6+. If you are not doing for React, Airbnb does publish another config that is without React, eslint-config-airbnb-base.

Eslint Plugin for React Native
On top of React, this plugin provide extra few linting particularly for React Native, such as no-unused-stylesno-inline-stylessplit-platform-components.

EslintPlugin for React Hooks
In React 16.8, Facebook introduce React Hooks, where you can use state and other features of React without a Class. It’s also allow sharing of state logic across components. Together with React Hooks, Facebook also introduce this Eslint plugin to enforce the rules of Hooks. If you plan to use Hooks, you are highly recommended to install this plugin.

Eslint Plugin for Jest
Jest is a testing framework that widely use for React and React Native applications, it’s easy to setup and most importantly, it has snapshot assertion that allow you to test for large object, for example, your React component. This plugin basically will help to guide and lint your Jest tests.

Eslint Plugin for Flowtype
Flowtype is the static type check for Javascript, it’s allow you to run checking of your code for any type error and missing props. In this plugin, it’s basically check for all the naming convention of type, duplication of type, missing type and etc.

That is all the basic Eslint plugins and configs for React Native, but what if you are working with many React Native projects and you want to adopt these Eslint plugins into all the projects? You will need to install all of them one by one and rewrite all the rules again? I beg you will get yourself crazy with that….

Create Shareable Eslint Config

To avoid duplicate work on settings up Eslint in your projects, you can create a shareable config that allow you to keep all your projects using the same Eslint configuration. First, let’s start with create a new Node.js module by create a folder and run npm init, remember to name the package with eslint-config-<your config name> as that is the standard for Eslint shareable config.

Next, create a index.js and put all your rules in the file, for example:

module.exports = {
  extends: [
    'airbnb',
  ],
  plugins: [
    'react',
  ],
};

To test your config before publish, you can run¬†npm link¬†in your shareable config folder, and run¬†npm link eslint-config-<your config name>¬†in the project that you want to use the config. Then, you need to update your project’s¬†eslintrc.js¬†by extending the shareable config.

{
    "extends": "eslint-config-<your config name>"
}

Now you should able to test your shareable config in your local machine. Before you want to publish your shareable config, you need to add the dependencies in package.json, at least, you need to define the minimum Eslint version you depending on.

"peerDependencies": {
    "eslint": ">= 3"
}

Now, you can run npm publish and your shareable config are now ready to use! In the end of the reading, I hope it’s give you some clues on how to create a shareable config for your project or organisation.

Lastly, I have created the shareable Eslint config for React Native, eslint-config-react-native-standard, please feel free to use or contribute. Thank you for reading!

Automatically Set iOS Build Number And Android Version Code In Unity Cloud Build

Did you ever have an experience where you automate the build of your games with Unity Cloud Build, but eventually you realise that you forgot to update the build number for iOS and version code for Android? Then, you have to edit the PlayerSettings and wait for the build again….

If you have a same nightmare, don’t worry, i have a solution for you.

I think many of you already know that if you set Android version code to 0 in PlayerSettings, Unity will help to populate the version code with “build number” from the Unity Cloud Build. However, that is not the case for iOS. Luckily, you can use PreExport(UnityEngine.CloudBuild.BuildManifestObject manifest) method to make some modification before Unity export the build.

First, create a file in Asset > Editor > AutoIncrementVersionCodeInCloudBuild.cs and put the following code in the script

public class AutoIncrementVersionCodeInCloudBuild : MonoBehaviour
{
#if UNITY_CLOUD_BUILD
    public static void PreExport(UnityEngine.CloudBuild.BuildManifestObject manifest)
    {
        string buildNumber = manifest.GetValue("buildNumber", "0");
        Debug.LogWarning("Setting build number to " + buildNumber);
        PlayerSettings.Android.bundleVersionCode = int.Parse(buildNumber);
        PlayerSettings.iOS.buildNumber = buildNumber;
    }
#endif
}

You will notice that I wrap the whole function with #if UNITY_CLOUD_BUILD because the BuildManifestObject can only access in Unity Cloud Build. This is very important if you wish to build the game in your local machine too. You may refer to the Github repo‚Äč for complete scripts.
Next, you need to tell Unity Cloud Build to run the PreExport method. Go to your Unity Cloud Build console > Cloud Build > Config > Advanced Settings and put the value “AutoIncrementVersionCodeInCloudBuild.PreExport” in PreExport Method Name.

Automatically Set iOS Build Number And Android Version Code In Unity Cloud Build

Now if you build again, you should see your APK or iPA will fill the version code or build number with Unity Cloud Build’s build number. Now, you no need to worry about updating the those values every time!

Transparency Sort Axis For 2D Top Down Games

You may want to read our previous blog about Sorting Layer and Sorting Group before continue reading this.

If you are developing for 2D top down games where the sprites will move from top to bottom or vice versa, sorting layer and sorting group may not enough for you. You will probably face some problem when one of your sprite is moving from top to bottom of another sprite.When the green T-Rex is moving from the bottom to the top of brown T-Rex, it’s suppose to render behind brown T-Rex but it does not. Luckily, Unity 3D provided a very convenient settings to solve this problem, it’s called the Transparency Sort Mode and Transparency Sort Axis.

These settings can be found from Edit > Project Settings > Graphics. By changing the Transparency Sort Mode to Custom and Transparency Sort Axis’s Y-axis to 1, it’s tell Unity3D to render the sprites with higher Y position at the back of screen. Now we can see the green T-Rex will render at the back of the brown T-Rex when we move it up.

That’s all for today! See you guys again.

Sorting Layer And Sorting Group For 2D Games In Unity 3D

When building on 2D games with Unity3D, one common issue that we will be dealing with is the sorting the layer of the sprites. In the common way, we can use Sorting Layer and Order in layer to solve most of the 2D game sorting issues.

You can find the sorting layer and order in the Sprite Renderer component of your sprite. By clicking the sorting layer drop down, you can assign the sprite to any layer such as foreground, mid-ground, background and etc. If you want to create or delete the layer, you may select Add Sorting Layer which will bring you to the Tag and Layer window to edit modify your sorting layer.

In some cases, you may want to sort different sprites within the same layer, for example, a grass sprite in front of a rock sprite. In this case, you can use Order in layer to sort the sprites within the same layer, the sprite that has higher number of the order will render in the front.

Sorting Group For Nested Sprites

Sometimes, you may have a nested sprites which contains different part of an object, for example, head, legs, body and etc. If you have more than 1 nested sprites in the same sorting layer, you may have some issues when the nested sprites is overlapping with each other, just like the cute T-Rexes below:

You will notice that when the green T-Rex is overlapping with another T-Rex, the head and the leg of 2 T-Rex is not sorting in the right way, they just look like dislocated from the body. To fix this issue, we will need to use Sorting Group ‚Äčfor all the nested sprites.

By adding the Sorting group components in the T-Rex, all the child sprites will be sort accordingly with the correct order like a  single sprite. If we try to move them around, they will just overlapping each other as a whole.If you are creating 2D platformer game using Unity 3D, using sorting layer and sorting group will help you sort different sprites in the correct order. That’s all for today! Hope this tutorial help you and have a nice day!

If you are developing a 2D top down game, we would like to recommend you to read our tutorial about Transparency Sort Axis and Transparency Sort Mode

Tile-Based Movement Using iTween

Shiba’s Adventure¬†is the new roguelike RPG game that we developed in¬†iOS¬†and¬†Android¬†that using tile-based movement. The tile-based movement is very easy to implement and today we would like to share with you how do we achieve the movement like this:

The secret recipe that we use is iTween! (Oopsss…it’s no longer secret now). iTween is lightning fast, lightweight and FREE animation system that makes implementing animation easier than ever. To implement tile-based movement with iTween, you just need to use MoveTo(GameObject target, Hashtable args)

// Assume the distance between tiles is 1f
Vector3 targetPosition = transfrom.position + new Vector(1f, 0f, 0f);
iTween.MoveTo(gameObject, iTween.Hash(
    "position", targetPosition,
    "time", 0.2f,
));

The code above allow you to move your game object from tile to tile. However, if you wish to make “jumping” effect thru the tiles, you can can use path‚Äč.

// Assume the distance between tiles is 1f
Vector3 targetPosition = transfrom.position + new Vector(1f, 0f, 0f);
Vector3 middlePoint = (transfrom.position + targetPosition) / 2;
iTween.MoveTo(gameObject, iTween.Hash(
    "path", new[] { middlePoint, targetPosition },
    "time", 0.2f,
));

Now you will have a “jumping” effect when moving across the tiles.
‚Äč
iTween is really a great tools that simplify implementing animation in Unity3D. You can download iTween from Asset Store and check out the documentation.  

Taking High Quality Screenshots From Unity 3D Editor

Sometimes, you may need to take a good looking screenshot of your game for successful press kit or artwork. It can be tricky and tedious to take a high-resolution from your Unity 3D editor. But don’t worry now, we found a great Unity plugin that can help you solve the problem.

Install the Instant Screenshot by Saad Khawaja from Asset Store and you can take fullscreen high-resolution screenshot quicker and easier. The plugin is very lightweight and easy to use.

The plugin allows you to use the camera functionality such as culling mask to exclude objects in some layer too. If you need a transparent background, you can change the “Clear Flags” of your camera to “Solid Color” and and check on the “Transparent Background” in the Instant Screenshot options.

Most of the people will find it very difficult to find the right angle and position for the camera. Here are some tips for you to configure your camera easily. In the Scene view, use the Hand tool to find the angle of your view. Once you found the perfect position and angle for your view, while highlighting your Camera object, go to “GameObject” and select “Align with View”, then your Camera will have the same view with your Scene view!

In summary, Instant Screenshot is an awesome tools that can save your times to capture high quality screenshots for your pre-production or post-production work!

Unity 3D Project Folder Structure Practice

Greetings everyone, today I would like to share with you regarding my experience on Unity 3D project structure when I develop Color Fiesta. Initially I created all the folders under Assets folder just like this:

I distribute the folder based on the type of files such as Animations, Prefabs, Sprites and etc. Everything is so neat and arranged accordingly at this moment until…..I starting to add other packages from Unity Asset Store.

Every time I added the new package, it will added in the root of the folder and everything is messed up. If I continue added more packages, it will be more chaos. Eventually, I decided to create another folder and move all my non-package folders into this particular folder.

Hooray! Now everything is back to normal again, nice and easy!