ゆるふわでJSを書いていてもできる!?React Nativeを使ってiOSアプリをつくってみた。

React Nativeを使って側アプリ検証用のWebViewだけのアプリをつくってみました。

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

React Nativeとは、JavaScript(React)でiOSやAndroidのネイティブアプリを作成するフレームワークです。基本的なお作法はReactそのままですが、React Nativeで用意されている<View><TextInput>といったネイティブっぽいコンポーネントを使って構築します。

今回はいわゆる「側アプリ」のWebView部分を、iOSのSimulator.appなどで簡単に動作検証できるよう、指定したURLをWebViewで表示するだけのアプリをReact Nativeでゆるふわに作成してみました。

この記事で使ったバージョンは次の通りで、シミュレータでの起動までを扱います。

  • Node.js: v12.4.0
  • React Native: 0.59.8
  • Expo: 33.0.7

下準備

公式ドキュメントを見ると「ウェブ由来の人はExpoツールを使いましょう」ということなので、まずはExpoをインストールを進めます(Node.jsはインストールされている前提です)。

参照:Getting Started · React Native

Expoはプロジェクトの作成やシミュレータの起動からビルドなどを担うもので、XcodeやAndroid Studioを使うことなく開発を進めることができるツールです。

参照:Expo

npmを使ってExpoをインストールします。

npm install -g expo-cli

またWebViewがReact Nativeのコアから外されるようなので、別途React Native WebViewをインストールしておきます。

参考:WebView · React Native

npm install -S react-native-webview

プロジェクトの作成

インストールできたらプロジェクトを作成します。(コマンド中の「GawaApp」はプロジェクト名なので、任意の名前を入れてください)

expo init GawaApp

少し待つと、「Choose a template:」とスケルトンを作成するテンプレートの選択するように指示が出ます。今回はタブバーは使わないので、一番上の「Blank」を選択します。

続いて「Please enter a few initial configuration values.」ということで、ホームスクリーンで表示されるアプリの名前を、「The name of your app visible on the home screen」の部分に下線がある状態で入力してください。

? Please enter a few initial configuration values.
  Read more: https://docs.expo.io/versions/latest/workflow/configuration/ › 50% completed
 {
   "expo": {
     "name": "<The name of your app visible on the home screen>",
     "slug": "GawaApp"
   }
 }

入力してEnterキーを押すと、必要なモジュール類のインストールが始まりますので、少し待ちましょう。

To get started, you can type:

  cd GawaApp
  npm start

と表示されたらインストール終了です。

ビルド

表示されている通り、cd GawaAppで「GawaApp」ディレクトリへ移動し、npm startでプロジェクトのプレビューを開始します。

少し待つとブラウザでExpoのDevToolが立ち上がります。

ブラウザでExpo DevToolが表示される

左側に「Run on iOS simulator」という項目があるので、クリックするとSimulator.appが立ち上がり、Expoアプリのインストールと起動がなされます。(Simulator.appの起動が失敗することがあるので、あらかじめ立ち上げておいたほうがいいかも)

ウィンドウ左側にある、iOS Simulator.appを起動するリンクをクリック

初回起動時に表示される画面で「Got it」をタップ

初めてExpoアプリを立ち上げた場合、『「Command + D」でいつでもこの画面に戻れるよ!』という説明があるので「Got it」をタップして進めると、テンプレートで作成したアプリがお目見えします。

ライブリロードになっているので、ディレクトリにある「App.js」を編集して保存すると、即座に書き換わります。

エディタでファイルを変種して保存すると、シミュレータで変更がすぐに反映される

エラーが出た場合は、エラー部分を修正してCommand + Rでリロードできます。(同期がうまくいかないのか、何度かリロードが必要な場合があります)

側アプリをつくる

URLを入力してボタンをタップすると、モーダルが下からスライドインしつつ、WebViewでページを表示するコードは次の通りです。単純なものなので、1ファイルにまとめています。

import React from 'react';
import { WebView } from 'react-native-webview';
import {
  StyleSheet, 
  Text, 
  TextInput,
  View,
  Button,
  Modal,
  SafeAreaView,
  } from 'react-native';

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isShowWebView: false,
      accessURL: '',
      isLengthExists: false,
    }
  }

  toggleGoButton(text) {
    if (0 !== text.length) {
      this.setState({isLengthExists: true});
    } else {
      this.setState({isLengthExists: false});
    }
    this.setState({accessURL: text});
  }

  toggleModalDisplay(visible) {
    this.setState({
      isShowWebView: visible
    });
  }

  render() {

    return (
      <SafeAreaView style={styles.container}>
        <View style={[styles.viewURLInput]}>
          <TextInput 
            autoCapitalize='none' 
            keyboardType='url' 
            placeholder='Enter URL' 
            placeholderTextColor='#888888' 
            returnKeyType='go' 
            onSubmitEditing={(e) => this.toggleModalDisplay(!this.state.isShowWebView)} 
            onChangeText={(text) => this.toggleGoButton(text)} 
            clearButtonMode='while-editing' 
            style={styles.viewURLInput__url} 
            autoFocus={true}
          />
          <View style={styles.viewURLInput__button}>
            <Button 
              disabled={!this.state.isLengthExists}
              title='Go!' 
              accessibilityLabel='Access to inputed URL' 
              onPress={() => {this.toggleModalDisplay(!this.state.isShowWebView)}} 
            />
          </View>
        </View>
        <Modal 
          style={styles.container}
          visible={this.state.isShowWebView}
          animationType='slide'>
          <SafeAreaView style={styles.viewModal}>
            <WebView
              source={{uri: this.state.accessURL}}
              useWebKit={true} 
            />
            <View style={styles.viewModal__aside}>
              <Button 
                title='Close' 
                onPress={() => {this.toggleModalDisplay(!this.state.isShowWebView)}} 
              />
            </View>
          </SafeAreaView>
        </Modal>
      </SafeAreaView>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  viewURLInput: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'stretch',
    padding: 16,
  },
  viewURLInput__url: {
    textAlign: 'left', 
    borderBottomWidth: 1, 
    paddingVertical: 8,
  },
  viewURLInput__button: {
    top: 16,
  },
  viewModal: {
    flex: 1,
  },
  viewModal__aside: {
    paddingBottom: 24,
  },
});

とりあえず動くようになったコードなので、ピヨピヨしていると思いますが…そこは何卒。

Viewの部分で使ったコンポーネントは、

  • StyleSheet
  • Text
  • TextInput
  • View
  • Button
  • Modal
  • SafeAreaView
  • WebView

で、おおよそ名前でどのような役割なのかは想像がつくのではないかと思います。

中でもStyleSheetは、コンポーネントのスタイルをHTMLのスタイルシートのように記述できるコンポーネントです。レイアウトにはFlexboxを使いますが、少し書き方がCSSのそれとは若干異なります。例えば、

  • Flexboxを適用するにはdisplay: flexではなくflex: 1
  • flexDirectionのデフォルトはcolumn(=縦に並ぶ)

などなど、詳しくはドキュメントを参照してください。普通(?)のCSSに馴染んでいると挙動が謎いです……

あと、WebViewで表示しようとしているページが、オレオレ証明書など使っている「https」だとATS(App Transport Security)のおかげでエラーが出ます(React Nativeだけでできる回避方法がわからなかったので、だれか詳しい人)。


雰囲気でJavaScriptを書いている私でも、これぐらいのアプリであればドキュメントを参照しながらつくることができました。

ぜひ試してみてくださいね!