-
React Nativeの認証フローを考える
React Nativeのアプリ開発やReactのSPA開発において認証フローは、クオリティを左右する重要な要素であることは間違いない。 実際、検索結果には、認証フローに言及した記事や質問が投稿されていて関心の高さが伺える。その内容としては、ルーター設計における認証フローに関するものが多くを占めている印象だ。 そしてReact Nativeのルーター設計には、React Navigationやreact-native-router-fluxが使われるが、いずれもルーター・ナビゲーションライブラリであって認証ロジックを提供していない。すなわちルーターで良しなに対応するか、自作の認証ロジックをこしらえる必要がある。 このエントリーでは、React Nativeの認証フロー について考えてみたい。 そもそも… そもそもReact Navigationの公式ドキュメントに、認証フローに言及したページがある。これはナビゲーションロジックを駆使して認証コンポーネントとプライベートコンポーネントを振り分けるというものだ。 まったくもって理にかなう認証フローだと思う。 その内容で十分だと思うならば、そちらを参考にするのがよいだろう。ただし認証ロジックを提供するものではないことは理解しなければならない。 認証フローのデモンストレーション AppScreen.js AppNavigator定数がメインナビゲーションに該当する。認証後に表示されるコンポーネントだ。AppNavigatorをcreateAppContainer(…)にセットした上で、withAuthenticator(…)にセットする。withAuthenticatorは、高階コンポーネントになっていて条件によって別コンポーネントを返す。 withAuthenticator.js まずconstructor(props) {…}で認証トークンを取得済みか否かを確認する。その結果で認証コンポーネントかプライベートコンポーネントに振り分ける。 認証トークン取得済みであればprops.authenticate(true);を実行して、this.props.isAuthenticatedの状態を更新する。これによってif (isAuthenticated) return <Component />;のコンポーネントが返される。すなわちこれは、AppScreen.jsで定義したcreateAppContainer(AppNavigator)を参照する。 認証トークン未取得であればprops.signIn();を実行して、this.props.authStageの状態を更新する。これによってswitch (authStage) {…}のcase types.SIGN_IN:が該当する。したがってサインインコンポーネントが返される。 authentication.js Reduxのaction-creatorとaction-type、reducerをまとめている。いわゆるDucksというデザインパターンだ。 stateにはisAuthenticatedとauthStageを定義している。この状態を更新するためにaction-creatorのforgotPassword、signUp、signIn、authenticateがある。 forgotPassword、signUp、signInは、authStageの状態を担当し、action-typeの定数値に更新する。 authenticateは、isAuthenticatedの状態を担当し、真偽値を受け取って更新する。 まとめ React Nativeの認証フロー に関する内容だった。 この内容ではReact Navigationを使っているが、react-native-router-fluxであっても再現可能だ。AppScreen.jsのwithAuthenticator(…)にreact-native-router-fluxのルーターコンポーネントをセットすればよい。 ただこの認証フローにも悩みどころがある。 React Navigationかreact-native-router-fluxに関わらず、認証コンポーネントはナビゲーション管轄外になっていることで、ヘッダーが表示されない。認証画面などにもヘッダーが必要ならば、NativeBaseなどのUIコンポーネントでヘッダーUIを挿入したり、各コンポーネントもナビゲーション化しなければならない。 UIコンポーネントを使う手段は、ヘッダーUIに差異が発生するから却下だろう。ナビゲーション化は、せっかく振り分けたのに、どこかモヤモヤする。 素直にReact Navigationの認証フローに倣ったほうがよいのかもしれない。 このエントリーが、あなたのクリエイティビティを刺激するものであると期待したい。
-
React NavigationのcreateBottomTabNavigatorでヘッダーUIを表示する方法
React Navigationのルーティング設定で、ヘッダーUIとタブバーUIを含むとき、タブ切り替えナビゲーションを担うcreateBottomTabNavigatorを使う。 ところが、createBottomTabNavigatorではヘッダーUIが表示されない。 タブバーUIにおいて、ヘッダーUIが不要なケースがあるかもしれないからヘッダーコンポーネントを含めていないと思われる。しかしその一方でcreateStackNavigatorだとヘッダーUIが表示される。 感の鋭いひとは、気づいたかもしれない。createBottomTabNavigatorとcreateStackNavigatorを複合的に使うことでヘッダーUIとタブバーUIの配置を実現できるということに。 このエントリーでは、ヘッダーとタブバーの要件を満たすReact Navigationのカスタマイズ方法を紹介したい。 createBottomTabNavigatorでタブバーUIを表示する 以下ソースコードを見てほしい。 createBottomTabNavigator APIを使っている。createBottomTabNavigatorの引数には、オブジェクト形式でHome, Cart, Settingプロパティが確認できる。各プロパティには、さらにオブジェクト形式でscreen, navigationOptionsプロパティがあり、screenに任意のReact.Componentを適用して、navigationOptionsでタブラベルを設定している。 タブバーUI設置は実現しているが、例によってヘッダーUIは表示されない。 createStackNavigatorでヘッダーUIを表示する 以下ソースコードを見てほしい。 行数が増えているが、実は、いたってシンプルだ。前述のソースコードからの変更点は、screenプロパティにcreateStackNavigatorを書いている。以下、差分キャプチャを見てほしい。 createBottomTabNavigatorのscreenにcreateStackNavigatorを展開しているだけだ。オブジェクトがネストして分かりにくさを覚えるが、createBottomTabNavigatorとcreateStackNavigatorのコンポーネントを通して任意のコンポーネントが表示される仕組みになっている。 果たして、タブバーUIとヘッダーUIを表示することができた。 まとめ ヘッダーUIとタブバーUIの要件を満たすReact Navigationのカスタマイズ方法の紹介だった。 「createBottomTabNavigatorのscreenにcreateStackNavigatorを展開している」と表現して、わたし自身があらためてスッキリした。ネストが多くてややこしいのだけれど、createBottomTabNavigatorのつもりで記述を済ませてからcreateStackNavigatorを書き加えると迷子になりにくいかもしれない。 このエントリーが、あなたのクリエイティビティを刺激するものであると期待したい。
-
[React Native] keyboardType propsのキーボードビジュアルの違い
アプリケーションのテキストフィールドのキーボードが、入力タイプによって最適化されることを思い出せるだろうか。メールアドレスフィールドだったり、電話番号フィールドだったりがわかりやすいかもしれない。メールアドレスを入力する場合は、@やドットが配置されていて、電話番号を入力する場合は、数字や#*+等の記号が配置されている。 このようにユーザーが入力しやすい機構が用意されているわけだが、テキストフィールドの入力タイプはエンジニアが明示しておく必要がある。 HTMLならば、<input>のtype属性にtextやpassword、numberなどを指定して、React Nativeならば、<TextInput>のkeyboardType propsにdefaultやnumber-padなどを指定するとキーボードが最適化される。 このエントリーでは、keyboardType propsの指定におけるキーボードビジュアルの違いを確認したい。 キーボードタイプの対応表 default number-pad decimal-pad numeric email-address phone-pad ascii-capable numbers-and-punctuation url name-phone-pad twitter web-search visible-password default number-pad decimal-pad numeric email-address phone-pad ascii-capable numbers-and-punctuation url name-phone-pad twitter web-search visible-password まとめ keyboardType propsの指定におけるキーボードビジュアルの違いについて確認してきた。プラットフォーム間においても、未対応のものがあって案件によって使い分けを検討するケースもあるかもしれない。とはいえ、適切なキーボードタイプの設定は、ユーザビリティ向上が見込めるため積極的に適用すると良さそうだ。
-
[react-native-router-flux] フォーカスされてるタブのアイコンにアクティブカラーをつける方法
react-native-router-fluxを使ってRouter設計するとき、タブバーUI用にTabsコンポーネントがある。アプリのスクリーン下部にあるナビゲーションUIのことだ。 タブバーデザインにおいてよほどの理由がない限りは、Tabsコンポーネントを使う。UIコンポーネントを利用すれば同様のものが設置できるし、自作タブバーにすることもできるが、わざわざそうする理由もない。なにより遷移設定が面倒くさい。 そんなTabsコンポーネントであるが、アクティブ時のカラーや非アクティブ時のカラーをカスタマイズするpropsがある。これを書き加えることで、タップしたときに色を変えられるわけだ。 しかしアイコン表示において手こずった事案があった。 このエントリーでは、フォーカスされているタブのアイコンにアクティブカラーをつける方法を共有したい。 react-native ^0.59.8 react-native-router-flux ^4.0.6 native-base ^2.13.4 タブバーにアイコンを表示する まずはRouter設定をする。使うコンポーネントは、 <Router> <Tabs> <Scene> の3つ。タブは「ホーム、カート、設定」を切り替える単純なものを想定している。 上記ソースコードのコメントアウト行を見てほしい。 各タブにアイコン設置するため、<Icon>を使っている。これはNativeBaseのIconコンポーネントだ。<Scene>にそれぞれ設定することでアイコンが表示される。以下のようになると思う。 つづいては、タブがフォーカスされたときにアクティブカラーを着色する工程だ。 タブがフォーカスされているとき(されていないとき)のカラーを指定する タブをタップしてフォーカスされたときに、テキストおよびアイコンがアクティブであるとわかるようにカラーを変えたい。 上記ソースコードのコメントアウト行を見てほしい。 activeTintColor propsがアクティブ時のカラーコードで、inactiveTintColor propsが非アクティブ時のカラーコードを設定している。いずれのpropsも文字列を受け付ける。 現在フォーカスされているホームタブのテキストはアクティブカラーになっている。ところが、アイコンのカラーは変わっていない。それもそのはずで、外部のコンポーネントなのだから仕方がない。 <Icon>は、NativeBaseのコンポーネントだった。アイコンのカラーを変更するには、style propsにオブジェクト形式で、CSSを記述すればよい。 上記ソースコードのコメントアウト行を見てほしい。 <Scene>のicon propsに設定した関数の引数から、focusedを受け取っている。これは真偽値が格納されている。つまりタブがフォーカスならtrueというわけだ。 style propsに、focusedがtrueならアクティブカラーで、falseなら非アクティブカラーがを渡している。これにてアイコンにもアクティブカラーを付与することができた。 まとめ フォーカスされているタブのアイコンにアクティブカラーをつける方法の共有だった。 この内容では、NativeBaseのIconコンポーネントを使っているが、自作のsvgアイコンやほかのUIコンポーネントであっても同様のことができるはずだ。focusedを受け取ってフォーカスか否かを確認して切り替えればよいのだから。 このエントリーが、あなたのクリエイティビティを刺激するものであると期待したい。
-
[React Native] ExpoのPush Notificationsでプッシュ通知する
このエントリーでは、ExpoのPush Notifications APIの実装、そしてプッシュ通知のテスト送信まで一連を紹介したい。 難しいのでは?と思ったあなたも大丈夫。 私は最近までプッシュ通知とは無縁のマークアップエンジニアで、スマホアプリ開発の経験といってもハイブリッドアプリ開発に携わった程度のものだ。プッシュ通知にどのような手続きが必要なのか全く無知な状態だったわけだが、テスト送信までした所感として、「簡単」だと言い切れる。 というのも結局は手続きというものはなく、証明書作成が不要だ。またAPNs/GCMといったプッシュ通知サービスへのリクエストもExpoが良しなにこなしてくれるため、条件分岐も不要だ。 ともあれ体感してもらうのが早かろうと思う。 コーディング App.js 重要なAPIは、NotificationsとPermissions、Constantsの3つ。デバイストークンを取得するまでの処理が記述されている。トークン取得に関わる処理はregisterForPushNotificationsAsync()メソッドに書かれていて、Appコンポーネントがマウントされた時点で、実行される。 デバイストークンを取得 プッシュ通知は「”◯◯”は通知を送信します。よろしいですか?」のアラートを許可することでデバイストークンが得られる。アラートに「ExponentPushToken[**********]」が表示されていれば、ひとまず成功だ。デバイストークンはテスト送信ツールに入力するため、メモしておこう。 プッシュ通知をテスト送信 「Push notifications tool」に通知したい内容を入力する。EXPO PUSH TOKEN (FROM YOUR APP)のデバイストークンが必須項目で、あとは適宜必要なものは入力するとよい。今回のプッシュ通知は、MESSAGE TITLEとMESSAGE BODYのみ入力している。Send a notificationボタンをクリックすれば、プッシュ通知サービスに送られる。 EXPO PUSH TOKEN (FROM YOUR APP) MESSAGE TITLE MESSAGE BODY Play sound JSON DATA TTL (SECONDS) IMAGE AUDIO (IOS ONLY) VIDEO (IOS ONLY) iOS MESSAGE SUBTITLE MESSAGE CATEGORY BADGE COUNT…
-
Expo Clientアプリにプロジェクトが表示されないときの解決方法
create-react-native-appでプロジェクト作成して、Expo (Run on iOS simulator) で確認する。デバッグには欠かせない一連の作業だ。 ところが想定していたプロジェクト画面ではなく「There was a problem loading the requested app.」のメッセージが表示される。 なんのことか分からず$ create-react-native-app my-new-projectによるプロジェクト作成ではなく、$ expo init my-new-projectを試すが、結局「There was a problem loading the requested app.」メッセージが表示される。 このように見に覚えもなくExpo Clientに表示できなくなってしまった場合には、手動による作業が必要になるようなのだが、ドキュメントにも書いてないことは分からなくても仕方がない。 このエントリーでは、「There was a problem loading the requested app.」に関わる問題の解決方法を紹介したい。 問題の画面 「There was a problem loading the requested app.」のメッセージが確認できる。読み込み中に問題が発生したってことらしい。 つづいて下段落に目を移すと「The experience you requested requires a newer version of…
-
NativeBaseの’Roboto_medium’フォントをプリロードする方法
NativeBaseのText APIは、テキストを表示する場合に使うのだが、Androidの表示においては、事前にRoboto_mediumフォントを読み込ませなければならない。 さもないと、次のエラー内容が発生する。 fontFamily “Roboto_medium” is not a system font and has not been loaded through Font.loadAsync. Roboto_mediumがシステムフォントじゃないから、Font.loadAsync()で読み込むように、とのことだ。 iPhoneのfontFamilyはsystemなのだが、AndroidはRoboto_mediumが指定されている。当該フォントを読み込まなければならないが、fontFamily宣言前にロード完了しておきたいところだ。 このエントリーでは、「fontFamily “Roboto_medium” is not a system font and has not been loaded through Font.loadAsync.」の回避方法を紹介したい。 まずrender直後にif (!this.state.isReady) {…}でisReadyステートを判定する。trueでなければAppLoadingコンポーネントがマウントされる。 フォントは、Font.loadAsync()メソッドで読み込み、その内容をPromise.all([])に追加してreturnする。 AppLoadingコンポーネントのstartAsync propsに与えられたPromiseオブジェクトを解決する。 AppLoadingコンポーネントのonFinish propsが、startAsync propsの解決をトリガーにしてthis.setState({ isReady: true })を実施する。同時にAppLoadingコンポーネントをアンマウントする。 再renderされる。 if (!this.state.isReady) {…}を通過する。 目当てのJSXが表示される。 まとめ ‘Roboto_medium’フォントをプリロードする方法の紹介だった。「fontFamily “Roboto_medium” is not…
-
React NativeのUIコンポーネント「NativeBase」のButton UIが横幅いっぱいにならないようにする方法
React Nativeの開発で、デザインがよほどオリジナルではない限りUIコンポーネント「NativeBase」を使うケースは少なくないと思う。各プラットフォームのスタイル適用を省略できることがメリットだからだ。 なかでもButton APIは重宝されているのではないだろうか。そもそもButton APIはReact Native標準コンポーネントのView APIとTouchableOpacity APIをラップしたもので、タップすると半透明になり、Material Design風にスタイルされている。 これを独自コンポーネントで定義するのは、なかなか面倒で骨が折れる。よほどオリジナルなデザインでない限りは、使い勝手のよいコンポーネントAPIだ。 さてNativeBaseのButton APIを考えなしにView APIなどのレイアウトコンポーネント内に配置すると横幅いっぱいに表示される問題に遭遇する。fullプロパティを付与していないにも関わらずだ。 このエントリーでは、Buttonが横幅いっぱいにならないようにする方法を紹介したい。 まずJSX内に見えるContainerとContentタグもNativeBaseのコンポーネントAPIだ。いずれもReact Native標準コンポーネントのView APIが基になっている。 重要なところは、Contentタグのstyle={{alignItems:’flex-start’}}だ。CSSのalign-itemsプロパティにより左寄せにしている。そもそもalign-itemsは、縦方向に関わるものなのだが、View APIの主軸は縦方向だ。つまりflex-direction:columnのような状態になっている。 主軸が縦方向(column)の場合は、align-itemsは横方向に関わる指定に変わるため、flex-startで左寄せになったというわけだ。 これはContainerやContent以外のレイアウトコンポーネントであっても同様である。たとえばViewタグに内包されるような場合は、Viewタグにstyle属性を付与すればよい。 このエントリーが、あなたのクリエイティビティを刺激するものであると期待したい。
-
react-native-router-fluxのタブバーに通知バッジを表示させる方法
react-native-router-fluxには、タブUIを構成するTabs APIが用意されている。スマホアプリ特有のスクリーン下部に配置されたユーザーインターフェースで、各タブメニューは、それぞれのスクリーンを表し、Routerとして機能するわけだ。 ところで、いずれかのスクリーンで新着メッセージなどがあることを知らせるために通知バッジを使うことは一般的だろうと思う。そしてこのような場合、通知バッジの配置場所として、タブUIに寄り添う形にすることも有り得ることだと思う。 しかしタブUIを構成するTabs,Scene APIに通知バッジを表示させるであろうプロパティが見当たらない。 断念せざるを得ないのだろうか?別の箇所に配置せざるを得ないのだろうか? 否だ。通知バッジを反映させる専用のプロパティは無いが、少しカスタマイズすればタブバーにも通知バッジを配置することができる。カスタマイズといってもネイティブのソースコードではなく、あくまでJavaScript内の話だ。 このエントリーでは、React Nativeのナビゲーションライブラリ「react-native-router-flux」の、タブバーUIに通知バッジを表示させる方法を紹介したい。 通知バッジを配置する Scene APIのiconプロパティ(props)を見てほしい。ホームスクリーンのSceneタグのiconには、矩形用のViewタグでIconタグとTabbarBadgeタグ、TabbarBadgeTextタグが確認できると思う。TabbarBadgeとTabbarBadgeTextはstyled-componentsライブラリでスタイルするためのタグで、名称は任意だ。const TabbarBadge = styled.Viewとconst TabbarBadgeText = styled.Textが代入している行になる。 果たして、タブに通知バッジが表示されるはずだ。