Categories
Tutorial

React Native URL Scheme and Linking API

React Native is possible the best hybrid framework for building mobile app at the moment. Not just its performance but also the development experience it provides. If you build it well, one really can’t tell if your app is native or a hybrid.

I spent my last 3 month developing a complicated hybrid app using react native. It involves many network requests, forms, lists, etc. It was undoubtedly challenging, but the result was quite satisfying.

For my next project, I was given another chance to work with React Native. This time I need to build a “AppStore” kind of application, which allow the user to install apps and launch installed apps right from this custom “AppStore”.

To launch other Apps in iOS or Android, we must register custom URL Schemes in the Apps we wish to launch.

Register URL Schemes

iOS

On iOS, we need to add a key CFBundleURLTYpes to the Info.plist file within the Xcode project.

Under this key we add an array, and define 2 keys CFBundleURLName and CFBundleURLSchemes.

Under CFBundleURLName you need to add a unique string that identifies your bundle, I used the same string as my app’s bundle identifier and it worked fine. Under CFBundleURLSchemes you need to add another array, and put in your desired URL schemes.

Here is an example of such entry inside Info.plist:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>com.johnsonsu.example</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>johnsonsu-example</string>
        </array>
    </dict>
</array>

More info about iOS URL Scheme can be found on the official documentation.

Android

Android’s setup is similar to iOS. We need to add a custom intent-filter in the Android app’s AndroidManifest.xml file.

Here is an example of such intent-filter:

<intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT"/>
    <category android:name="android.intent.category.BROWSABLE"/>
    <data android:scheme="johnsonsu-example"/>
</intent-filter>

Since React Native’s Linking API will be creating an intent with action equals to android.intent.action.VIEW, your intent-filter must contain an action tag with this name.

The data tag is where you should define your custom URL Scheme. In this example I used the same one as the iOS scheme. Doing it this way allows me to use the exact same code in React Native to launch these applications in both iOS and Android.

More info about Android Intent Filter can be found on the official documentation.

Launching the App in React Native

If you setup your apps properly, you should be able to launch that app in React Native using:

import { Linking } from 'react-native';

Linking.openURL('johnsonsu-example://').catch(err => console.error('An error occurred', err));

If the OS can’t handle the URL you tried to open, the exception will be caught inside the catch block, and you should handle it properly to prevent a crash.

A safer way to open URL is to check if the OS can handle it before you open it. You can check it using:

import { Linking } from 'react-native';

const url = 'johnsonsu-example://';
Linking.canOpenURL(url).then(supported => {
  if (!supported) {
    console.log('Can\'t handle url: ' + url);
  } else {
    return Linking.openURL(url);
  }
}).catch(err => console.error('An error occurred', err));

This works on Android without any setup. However, on iOS 9 or above, you need to add LSApplicationQueriesSchemes to your React Native app’s Info.plist or Linking.canOpenURL(url) will always returns false.

This is an example entry in Info.plist:

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>johnsonsu-example</string>
    <string>youtube</string>
    <string>facebook</string>
</array>

There you have it, a simple way to launch any other apps from your React Native app!

Categories
Tutorial

Android Retrofit Posting Array

When you do a POST method on the popular plugin Retrofit on android, you normally will do something like this:

    // ApiService.java
    public interface ApiService {

        @FormUrlEncoded
        @POST("/api/login")
        public void login(@Field("email") String email, 
                          @Field("password") String password,
                          Callback<ApiKey> cb);
    }

    // MainActivity.java
    public class MainActivity extends ActionBarActivity {

        // function that calls the api service
        public void fetchProjects() {
        GlobalVariables.getRestClient().getApiService().projects(globalVariables.getApiKey(), new RestCallback<List<Project>>() {
            @Override
            public void failure(RestError restError) {
                Log.e(TAG, "failure");
                Log.e(TAG, restError.getError());
            }

            @Override
            public void success(List<Project> jsonProjects, Response response) {
                Log.d(TAG, "success");
            }
        });
    }

For details on how to do this, you can refer to this guide by Robin Chutaux.

However, when you try to POST an array, wether it’s a integer array or a string array. You might use something like this on the android client:

    // ApiService.java
    @FormUrlEncoded
    @POST("/api/projectLost")
    public void projectMarkLost(
        @Field("apiKey") String apiKey,
        @Field("project_id") int projectId,
        @Field("lost_project_remark") String lostProjectRemark,
        @Field("lost_project_reasons") ArrayList<Integer> lostProjectReasons,
        Callback<JsonObject> cb
    );

    // Content wish to post
    POST content:
    apiKey = EzAvFvScs45a2DRI0MktdIDeVRbP59
    project_id = 986
    remark = testing
    details = [detail_1,detail_2]

Then you will get something like this in the request boby:

    04-17 09:34:59.058    1037-1250/com.company.app D/Retrofit﹕ apiKey=EzAvFvScs45a2DRI0MktdIDeVRbP59&project_id=986&remark=testing&details=detail_1&details=detail_2

Which some web framework (I’m using Laravel) will have a hard time accepting it due to the double details variable is not in the proper format. Because the Retrofit library dosen’t transform your @Field("lost_project_reasons") to a array field when making the request.

To fix this problem, please add [] in the field name. For this example:

    // ApiService.java
    @FormUrlEncoded
    @POST("/api/projectLost")
    public void projectMarkLost(
        @Field("apiKey") String apiKey,
        @Field("project_id") int projectId,
        @Field("lost_project_remark") String lostProjectRemark,
        @Field("lost_project_reasons[]") ArrayList<Integer> lostProjectReasons,
        Callback<JsonObject> cb
    );

Notice the [] in field name of the 4th Field.

If you recompile your application now, your web framework should have no problem understanding your request.

Categories
Tutorial

Android Studio Customizations and Productivity Tips

It’s been a long time since the last time touched Android.
The official development IDE from Google used to be Eclipse. It is nice to see they switched to JetBrains, the new Android Studio.

JetBrains’ IDE are packed with all these awesome features like tree view filtering, fully customizable keymap, Emmet and so much more. It is also empowered by plug-ins that are developed by company or individuals.
All these come together to form a highly customizable IDE.

I believed that by customizing IDE properly, we can greatly increase our productivity.
Use the keymap that you are familiar to quickly open files and make Emmet to write basic code structures for you will save you a lot of time.
I also feel that an IDE with the colours and theme that you like can also help you be more productive.

Install Custom Themes

Android Studio can use any theme that are made for JetBrains IDEs. daylerees have a great GitHub page with tons of editor themes for different IDEs, including JetBrains.

He also have this nice page for you if you want to quick view of all the themes. My personal favourite is the TomorrowNight from a different repo.

To install custom themes, simply put the .icls file you download in

~/Library/Preferences/AndroidStudio/colors/

Then restart Android Studio, and select your new themes in

Android Studio > Preferences > Editor > Colors&Fonts

I recommend you to install the Color Ide plug-in. It will change the background colour of all tree view to match your theme.

Now the Android Studio will look a lot better.

Learn Vim

There is a very popular plug-in in that works on all JetBrains IDE — IdeaVim. It allows you to use your vim commands in the IDE, including your custom commands.

If you have not tried vim yet, I strongly encourage you to give it a shot. It will greatly increase your productivity, and not just in Android Studio, in every other text editing task as well.

Hide Menus and Use Keyboard

This is how my Android Studio looks like

Screen Shot 2015-03-12 at 12.29.45 PM

I like minimalistic. Minimalistic interface can force me to use my keyboard short cuts and my custom mappings. After you get use to it, you will notice how using the mouse can really slow you down.

You can turn off Tabs in the Preferences.

Screen Shot 2015-03-12 at 12.33.45 PM

I have remapped my “File” shortcut to Command+P (Sublime shortcut), Command+R for “File Structure”.
To open a different file, I just hit Command+P and start typing my file name.
To Navigate inside a file, hit Command+R and start typing your function name to get to the function.
Command+O will allow you to search for class using class names.
Command+E will give you a list of recently opened files in chronicle order.
Press Command+E then Enter will allow you to quickly toggle between the 2 files.

There is so much more that you can do with Keymap, but these are the ones I use the most. After you get used to them, you rarely need your Organizer sidebar.

Take the time and customize your IDE until you like it. It’s worth it.