Movable Type 6 + Apliko + React Nativeでブログリーダーアプリを制作

仕事で「Apliko(Movable Typeのアドオン)」の制作を担当しておりiOSアプリとアドオン開発に勤しむ日々ですが、iOS / AndroidアプリがJavaScriptで開発できるという「React Native - Build native mobile apps using JavaScript and React」に興味を持ち、3連休を利用して個人的に試してみることにしました。

React」はもちろん知っているのですが、使う機会もなく触るのは昨日が初めてです。「WEB+DB PRESS vol.102」の特集を読み、サンプルコードを改変して作っていくことにしました。

今日の成果

昨晩から少しずつ時間を使って進め、記事一覧の表示〜選択した記事の表示までが出来上がりました。

iOSで記事一覧画面を表示するとこのようになりました。
画面キャプチャ:iPhoneシミュレーターで出来上がったアプリを表示した画面

Androidで記事画面を表示するとこのようになりました。 画面キャプチャ:Androidシミュレーターで出来上がったアプリを表示した画面

動作の様子

react-native run-iosコマンドを実行し、iPhoneのシミュレーターでアプリを操作する様子を動画に収めました。

出来上がったコード

コードはWEB+DB PRESS掲載のものを改変していったので、記事一覧画面の表示コードのみご紹介します。

基本的には掲載されているコードと大きく変わりはありませんが、MTの記事を表示するために作成した記事オブジェクトを使えるように改変していきました。記事データはData APIから取得しています。なお、「react-navigation」のバージョンが上がっている関係でimportの書き方が掲載されているコードから少し変わっているようでした。

// @flow
import config from './config';
import React, { Component } from 'react';
import {
  Text,
  TouchableOpacity,
  FlatList,
  StyleSheet
} from 'react-native';
import Entry from './Entry';
import { getEntries } from './EntryService';
import type { NavigationScreenProp } from 'react-navigation';
import { sendBAActivity, sendLVActivity } from './AplikoActivity';

type Props = {
  navigation: NavigationScreenProp<*>,
};

type State = {
  entries: Entry[],
};

class EntryListScreen extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { entries: [] };
    sendBAActivity();
  }

  onPress(item: *) {
    const { navigation } = this.props;
    navigation.navigate('Entry', { entry: item });
  }

  componentDidMount() {
    getEntries(config.blogId)
      .then((entries) => {
        console.log('記事一覧取得完了', entries);
        sendLVActivity(config.blogId);
        this.setState({ entries });
      });
  }

  render() {
    const { entries } = this.state;
    console.log('render', entries);
    return (
      <FlatList
        data={this.state.entries}
        keyExtractor={item => item.id + ''}
        renderItem={({ item }) => (
          <TouchableOpacity
            onPress={() => this.onPress(item)}
            style={styles.itemContainer}
          >
            <Text>
              {item.title}
            </Text>
          </TouchableOpacity>
        )}
      />
    );
  }
}

const styles = StyleSheet.create({
  itemContainer: {
    padding: 8,
    height: 48,
    justifyContent: 'center',
  },
});

export default EntryListScreen;

Flow」が導入されていて静的型チェックを行っていることは興味深かったです。まだまだFlowの研究は必要ですが、普段Swiftも書いていることからJavaScriptに型を導入するのは難しくありませんでした。

また、一覧表示部分のスタイルをスタイルシートで書くことができるのも簡単で良いなと感じました。普段はストーリーボード(時々コード)でAuto Layoutを使ってレイアウトをしているのですが、これがなかなか難しいのです。JavaScriptやスタイルシートで書いたにもかかわらず、WebViewではなくネイティブUIでレンダリングされていることも驚きです。さらに驚きなのは、一度コンパイルしてシミュレーターに表示すると、JavaScriptのコードを書き換えてもcommand + Rでリロードされることです!
画面キャプチャ:アプリ画面の階層構造をビジュアルで表示した様子

Reactは初めてでしたが、いわゆる写経をしながら改変するだけであればJavaScriptの知識でなんとかなりました。

Aplikoとの連携

折角なので普段制作しているアドオンと連携を図ってみます。プッシュ通知の送信機能とアクティビティ機能あたりがすぐに使えそうですが、ひとまず今日はアクティビティ機能のみにします。

アクティビティ機能とはユーザーの行動をMovable Typeの管理画面上で確認できるようにする機能です。例えばアプリを起動した、○○の記事を見た、という行動です。今回はJavaScriptでアクティビティを送信する関数を書き、sendLVActivity(config.blogId);等でデータを送信しました。無事Movable Typeの画面上にアクティビティが表示されています。
画面キャプチャ:Movable TypeでiOSのアクティビティ一覧を表示している様子

もちろん同一のJavaScriptコードでAndroidでもアクティビティが送信されており、下記の通りMovable Typeの画面上にデータが表示されました。
画面キャプチャ:Movable TypeでAndroidのアクティビティ一覧を表示している様子

まとめ

React Nativeを使うと本当にJavaScriptの知識でネイティブアプリが作成出来ることに驚きました。ただ、やはりSwiftやJava(Kotlin)で開発した経験がある方が、アプリが上手く動かない時スムーズに対応できるかなと感じました。

Movable Type 7のData API 4.0の開発も進んでいるようなので(GitHubでちらっと見ました)、ベータ版が公開されたらコンテンツタイプを使ってアプリを作ってみたいなと考えています。