知識のリンク集

技術系アウトプット

はじめてドメインを登録した

インフラ周りの勉強を実際に手を動かしてやってみようと思いまずドメインを手に入れることにした。

お店はいくつかあるようだがお名前.comで買うことにした。

とりあえずhoteteで検索するとドメインの種類によって値段が全然違うということとなんかめちゃ安いことに驚く。

f:id:yuri_iOS:20191230173124p:plain

あまりの安さにテンションが上がりしばらく悩むも.トップレベルドメインはtokyoを選択。

東京に住んでないのに。なんかかっこいいんだもん。

同じドメインでも他の店舗だと値段が異なっていたが質などに差はなく、直接販売している(=レジストラ)か卸してもらって再販売している(=リセラ)という違いによるものらしい。

そして.tokyoは GMO ドメインレジストリが管理しているものとのこと。

Who is

ドメインの取得状況を知るには各レジストラが提供するサイトで知ることができる。

.tokyoの場合はここ

認証大事

ドメインを登録後、下記タイトルのメールが届きメールの有効性を確認してくる。

【重要】[お名前.com] ドメイン 情報認証のお願い

これに対応しないと自分のアカウントで登録してあるドメイン全てが停止されるので企業だったらちょっとした大問題になりそうな話である。

AWS CloudFormationをざっくり把握

新しく会社で動画をアップロードするサービスを立ち上げるにあたり、インフラ環境をCloudFormationで用意した。

動画をアップロードするというサービスの中でも、「動画自体のデータを圧縮し、貯めておく」「動画にまつわるデータを管理し、クライアントの要求に応じて必要な動画データを提供する」というさらに2つのサービスに分割してそれごとにCloudFormationを用意した。

この考え方はビジネス機能に沿った複数の小さいサービスの疎に結合された集合体として構成するというマイクロサービスに則っているのだが詳しくはまた別の記事で書きたい。

CloudFormationについてまとめた内容はこちら
community.inkdrop.app

Makefile

Lambda関数をgoで複数用意した際、メンバーがビルドとデプロイのコマンドをまとめてMakefileを作成してくれた。

このおかげでデプロイ時に複数のコマンドを叩かず1つのコマンドで済んだり、Circle CIのymlファイルを用意する際にも可読性が高くなるなどの恩恵があった。



本文はこちら
community.inkdrop.app

React Native 0.58.4 アップデート

0.58系列変更点

github.com


アップデート手順

1: react-native-git-upgrade

react-native-git-upgradeコマンド実行でエラー
f:id:yuri_iOS:20190206170450p:plain

.babelrcをreact-native-git-upgradeコマンド実行時のみファイル名を変更することで回避できる。
情報元: https://github.com/facebook/react-native/issues/21241

2月7日の時点ではReact Native 0.58.4, React 16.6.3に更新される。

2: node_modulesとバージョン管理ファイル類を削除

削除対象

  • node_modules配下
  • yarn.lock
  • ios/Pods
  • ios/Podfile.lock
3: 手動で調節

react-domとreact-test-rendererを16.6.3に
react-test-rendererとreact-domはreactにあわせてバージョンアップされている。
react-native-git-upgradeコマンドによってreactのバージョンをあげた際には常に他2つもバージョンをあわせててあげている。

react-native-modalbox は1.7.1に
0.58.xでBackAndroidがなくなったことによる修正を1.7.0で行っている。
問題: https://github.com/dooboolab/talktalk-rn/pull/13
修正: https://github.com/maxs15/react-native-modalbox/pull/262


4: npmモジュールの取得

コマンド "npm cache clean --force" でキャッシュを削除した上でコマンド "yarn" を実行。
※ yarnが古くないことを事前に確認してからyarnした方が良い。
 現在の安定バージョンは1.3.0

5: コンフリクト解消

iOS
App.xcodeproj/project.pbxproj
f:id:yuri_iOS:20190207193423p:plain
JavaScriptCore.frameworkがところどころ加わって既存のファイルとコンフリクトを起こしているので両方残す形で修正

Android
android/app/src/main/AndroidManifest.xml
少し差分が出るが実質

android:roundIcon="@mipmap/ic_launcher_round"

の項目が増えただけ。

6: ネイティブモジュールの取得

iOS
コマンド "pod install" を実行

Android
Android Studioにて "sync Gradle" を実行

7: ビルド

iOS
Product > Clean Build Folderを実行した上でRUN

Android
Build > Clean Projectを実行した上でRUN


Jest

RCTSafeAreaViewとTextのDOMツリーに変化がある。
実行は問題ないのでスナップショットを更新して終了。

RCTSafeAreaView
"emulateUnlessSupported={true}" が追加されている
f:id:yuri_iOS:20190207200839p:plain

Text
ellipsizeModeをしている箇所に "truncateLocation="smart"" が追加されている
f:id:yuri_iOS:20190207200939p:plain


Flow

flowのバージョンが "^0.86.0" にあがったのでpackage.jsonのflow-binのバージョンも^0.86.0にあげる


**アーカイブビルド
iOS
Xcode10でアーカイブビルドが失敗します。
新しいビルドシステムはまだ問題がおおいですね。
Podfileに3行記述を追加するだけで解決できます。
修正: https://github.com/facebook/react-native/issues/21583


最後に

ここから先、根拠のない情報で申し訳ないのですが誰かの参考になればと思い念のため紹介します。

iOSでアプリの起動が失敗する。
具体的にはビルドフェーズは成功し、パッケージャーもエラーはないのにシミュレーターが起動後スプラッシュスクリーンから進まないという状況になる。

私の場合下記手順でアプリが無事にスプラッシュスクリーンより先に進む。

1: Workspace SttingsからPer-User Workspace SettingのBuild Systemを一回 "Legacy Build System" にした上でビルド。
2: 下記エラーが出力され確実にビルドが失敗する。
f:id:yuri_iOS:20190207202436p:plain
3: 再度Workspace Sttingsを開いてPer-User Workspace SettingのBuild Systemを "New Build System" に変更してビルド。

react-native-webを自社アプリに組み込んでみた

自社プロダクトのweb版を作ることを検討するため、以前react-native-domを自社アプリに組み込んでみたのに引き続き、今度はreact-native-webを既存のプロダクトに組み込んでみて得た知見をまとめます。

react-native-webを使ってやりたいこと

react-native-webを使う理由として同じプロダクトのwebサービスを作るにあたり、「既存のソースコードの共有ができること」が大きいです。
下図のようなディレクトリ構成にすることで今後の開発でも最小限でモバイルにあわせてwebも開発していくことが可能になると考えています。
ピンクの枠で囲まれた部分がソースコードを共有する部分です。
f:id:yuri_iOS:20181209195114p:plain

reduxのコードを全部共有

APIを叩く処理、受け取ったデータの更新などデータフローに関する処理は基本モバイルもwebも同じで問題ないと考えています。
裏側の処理の実装もロジックテストも先にモバイルで開発した時に担保できていれば、後から作るwebでは画面のみ作るだけで済みます。
更に言えばモバイルで実装した人が同じ部分をweb版も実装すれば大幅な時間の節約が期待できます。

Atomic Designの思想でスタイルの統一

コンポーネントはatoms と一部のmoleculesを共有し、それより大きい粒度ではモバイルとwebわけて画面を組みあげたいです。
これができるとモバイルとweb両方作る身としてはにスタイルの書き方が全く同じで良いので気持ちが良いし開発スピードもあがります。
なによりパーツのトンマナが揃い、メンテナンスの余地が減るのはとても素晴らしいことだと思っています。

開発体験

上記2つによって、開発者体験はとても良いものになります。
ブランチを切り替えるだけで、いつもと同じスタイル記法、reduxの思考でそのまま気軽にweb版も開発できるのはとても素敵です。
モバイルはどうなってたっけ?webはどうなってたっけ?もエディターを開き直す必要はありません。

組み込み作業

ここから実際に組み込む際に行なった作業手順を紹介します。
コード量が多いのと会社のプロダクトに実際に組み込んだためソースコードを見せることができませんが、必要だと思うポイントを絞って記載します。

1. Entry Point (index.js)

import { AppRegistry } from 'react-native';
import Root from './root';
import registerServiceWorker from './registerServiceWorker';

AppRegistry.registerComponent('TeamHub', () => Root);
// 下記2行をweb用に追加
AppRegistry.runApplication('TeamHub', { rootTag: document.getElementById('root') });
registerServiceWorker();

2. Root
ProviderのstoreにconfigureStoreで生成したstoreを渡してreduxを組み込みます。
I18nはブラウザで設定されている言語の取得方法がわからなかったので一旦強制的に日本語にしています。

import React from 'react';
import { Provider } from 'react-redux';
import I18n from 'react-native-i18n';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import App from './app';
import configureStore from '../redux/configureStore';
import loadTranslations from '../i18n/translations';

const store = configureStore();
loadTranslations();

// 強制的に日本語に設定
I18n.locale = 'ja-JP';
if (I18n.locale === 'ja-JP') {
  require('moment/locale/ja');
}

const Root = () => (
  <Provider store={store}>
    <Router>
      <Route exact path="/:filter?" component={App} />
    </Router>
  </Provider>
);

export default Root;

3. redux/modules
ここの作業ボリュームが多いです。
webではネイティブモジュールを読み込んでいるファイルをコンパイルすることができないため、ネイティブモジュールを使用するreduxのモジュールは全て別を用意しました。
例) auth.js / auth.web.js
こうすることでreducer.jsでモジュールをcombineReducersでまとめ上げる際にweb.jsの方を読んでくれるのでネイティブモジュールの読み込みによるコンパイルの失敗を回避します。

具体的には、ログインやサインインなどの認証周りのモジュールをまとめたauth.jsというファイルの中でreact-native-fabricなどのネィティブモジュールを使用していたところ、そのモジュールを実際に使う処理を実行しなくてもimportの時点でモジュールを読み込みに行くため、そこでネィティブにアクセスする前提のモジュールにとって色々なものがなくてコンパイルが失敗しました。
f:id:yuri_iOS:20181209014937p:plain

ネイティブモジュール読み込めないよ問題はreact-native-domでも同じでしたがreact-native-webではこの回避策としてimportした時点でモジュールの生成をしないようにできるwebpackのIgnorePluginが使えるのではないかと考えました。
IgnorePlugin

IgnorePluginを使ってネィティブモジュールを全て登録しておき、ネィティブモジュールを使う処理だけモバイル環境でのみ実行するようにする最低限の変更で済むことを期待しました。

f:id:yuri_iOS:20181208222626p:plain
上記が実際に出力されたエラーです。
./node_modules/react-native-fabric/Crashlytics.jsを読み込まないように下記記述でコンパイルを試みましたがエラーは変わりませんでした。

new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/, /^\.\/Crashlytics$/, /react-native-fabric$/),

結果としてネィティブモジュールを読み込むファイルは重複するコードが多くなってしましますが全て別途用意することになりました。
react-native-domの時と違ってファイルの拡張子をweb.jsとすれば勝手に読み分けてくれるのは嬉しいです。

Webpack設定

webpackを使うために下記ファイルを用意しました。

  • webpack.config.dev.js
  • webpack.config.prod.js
  • env.js
  • paths.js
  • polyfills.js

公式ドキュメントにもありますが、webpack.configにてreact-nativeにエイリアスを貼ってreact-native-webを読み込むようにします。

module.exports = {
  resolve: {
    alias: {
      'react-native$': 'react-native-web'
    }
  }
}

reactn-native-webというのは基本react-nativeと同じ名前のパーツをweb用に用意しているので例えば

import { View } from 'react-native'

とあった場合エイリアスによってreact-native-webのViewを見に行きます。
React Native examples

webpackの設定は自分も初めてやったので確証はないのですが、多分このエイリアス以外特別なことはしてないと思うので他は割愛します。

devserver設定

同じくdevserverを使うために下記ファイルを用意しました。

  • webpackDevServer.config.js
  • registerServerWorker.js

Tips: PropTypes
PropTypesを使っているモジュールはコンパイルできないのでbabel-plugin-transform-react-remove-prop-typesを使うことで削除する必要があります。
またこの時babel-loaderの対象としてincludeにPropTypesを使うモジュールを指定する場合はpaths.jsでアプリのディレクトリからの相対パスに変換してくれるresolveAppを使った形でないとパスをうまく解釈できません。

//paths.js
module.exports = {
  ....
  appNodeModules: resolveApp('node_modules'),
  appRNNodeModules: resolveApp('node_modules/react-native-material-design'),
  appRNNodeModules2: resolveApp('node_modules/react-native-vector-icons'),
};
//webpack.config
{
  test: /\.(js|jsx|mjs)$/,
  loader: require.resolve('babel-loader'),
  include: [paths.appSrc, paths.appRNNodeModules, paths.appRNNodeModules2],
  options: {
    presets: ['react-native'],
    plugins: ['react-native-web', ['babel-plugin-transform-react-remove-prop-types', { mode: 'remove' }]],
    cacheDirectory: true,
    babelrc: true,
  },
},

react-native-webのプロダクション採用について

採用するにあたり懸念している点を列挙します。

web版でのスタイルの情報が曖昧

f:id:yuri_iOS:20181209053607p:plain
当たり前ですがToggle InspectorのようにViewにあてているスタイルの文言ががそのまま表示されません。
Elementsで該当箇所を指定すると遠からずなスタイルの表示も確かに出てはいますが、原因不明のレイアウトが崩れが起きた場合に原因を探すのは苦労しそうです。
特にブラウザによって異なる系問題はかなり苦労しそうです。

バージョン問題

モバイルでは最新バージョンがでたらすぐにアップデートしていたのですが、react-native-webはreact-native最新バージョンにすぐに追従してくれるわけではないのでバージョンが引っ張られる、もしくはwebの時だけ毎回バージョンを下げるという絶対めんどくさい作業が発生することになります。
バージョンで書き方が変わる箇所があるとなった場合にはこれはけっこうなネックになりえます。

エラーがわかりにくい

実際に他の人が触って見てもらった感想です。
基本問題があればコンパイルできないので開発最中は困ることはないと思いますが、バージョンアップ時や最初の0から構築する場合に苦労することになります。

共有できるコードが案外少ない

redux側はほとんどまんま使い回せるかと思いきや、アクションごとに数値を取るためにそこかしこにネィティブモジュールを使ってしまっていたのでけっこうredux側でも手を入れなきゃいけない箇所が多くなってしまいました。
表側に関してはそれこそほとんど別に組まなくてはならないのですが、感覚値完全に共有できるのは全コードのうち30%行かないくらいかな。。という感じです。

参考になる記事が少ない

日本だとなかざんさん(@Nkzn)のウォーターセルしか採用事例がなく、海外でも軽く作ってみた系はあってもがっつり使ってんでー!知見あるでー!というのは見つけられませんでした。

チームやプロダクトにあっていない

自分たちのチームにはwebに詳しい人がいないのでコンパイルできない時にwebpackの設定が悪いのかreact-native-webに原因があるのか自分たちのコードに原因があるのか判断するのが大変そうです。
またこのプロダクトは今後もたくさんの機能を開発予定で、既に多機能なアプリなので追加したい機能によってはいつかスタックする事案が出てきそうなのが怖いです。

最後に

react-native-webはTwitterUberThe Timesなども使っているので挑戦してもいうほど悪い賭けではないと思うのですが、やはり会社でやるには強くやろうぜ!と言えないです。
ここは強いパッションがないと採用するのは難しいのかな。
もっとシンプルなアプリケーションであれば、ReduxとAtomsを共有してモバイルとwebを作るという発想は最速プロダクトリリース手法として悪くないと思っています。

webpackのコンパイルでnode_modules配下のPropTypesを解決する

状況

既にたくさんのライブラリを使っているReact NativeのプロジェクトにReact Native for Webを組み込んでWebpackの設定をしていた際にprop-typesを使っているモジュールの解決ができずwebpackのコンパイルがこけた。

自分たちの書いているJsのコードで使用されているPropTypesは解決できているのでnode_modules配下だけ解決できていなかった。

PropTypesの排除

PropTypesについてはwebpackでコンパイルする前にbabelの方でprop-typesを取り除くという作業が必要になり、具体的にはbabel-plugin-transform-react-remove-prop-typesというプラグインを使う必要がある。

@.babelrc
{
  "plugins": [
    ["babel-plugin-transform-react-remove-prop-types", { "mode": "remove" }],  // <- Add
  ],

modeについては remove / wrap / unsafe-wrap の3つがある。
詳しくは下記で紹介されているがデフォルトはremove。
GitHub: babel-plugin-transform-react-remove-prop-types

原因の推測

これがnode_modules配下のみ効いていないということなのだから怪しいのはwebpackのloaderの設定の箇所だと絞り込める。

@webpack.config.dev.js 修正前
modules: {
    {
        test: /\.(js|jsx|mjs)$/,
        include: [paths.appSrc],
        exclude: /node_modules/,
        loader: require.resolve('babel-loader'),
        options: {
            presets: ['react-native'],
            plugins: ['react-native-web'],
            cacheDirectory: true,
            babelrc: true,
        },
    },
}

上記だとnode_modulesをbabel-loaderの対象外としている。
そこでPropTypesを使用しているnode_modules配下のライブラリを直接指定してbabel-loaderの対象としたい。

解決方法

これを解決するためには2つの手順が必要だった。

  • paths.jsに対象のライブラリを追加
@paths.js
module.exports = {
    appRNNodeModules: resolveApp('node_modules/react-native-material-design'),
    appRNNodeModules2: resolveApp('node_modules/react-native-vector-icons'),
}
  • includeにpathsに追加した名前を追加
include: [paths.appSrc, paths.appRNNodeModules, paths.appRNNodeModules2],

つまづいたのはここの記述方法

調査の最中そこかしこで下記のような記述をみかけた。
exclude: /node_modules/,

「node_modulesだけはwebpackがコンパイル後もきっとわかるということなのだろう」と思いここで
include: [paths.appSrc, /node_modules/react-native-material-design],

のように記述していたがこれが動かなかった。

公式ドキュメントを見ても詳しく記述方法に注釈がなかったので(もしかしたら最初から読めばお作法として書かれていたのかもしれない)ずっとはまっていた。

もしここについて詳しい方がいたら、また記事の内容に間違いがあれば気軽にコメントお願いします。
まさかり歓迎という言葉が好きではないので、バッテリー募集(投げてくれたらしっかり受けめるよ)と言っておきます⚾️

最後に

これについて、react nativeではPropTyepsはなくす方向なのに未だにPropTypesを使っているような活発でないreact-native-material-designは使うべきではないのではという話になった。
その際下記のnpmモジュールの利用率を調べることができる便利なサービスを見つけた。

www.npmtrends.com