wgetでサイズ確認
ものすごく小さな話かつググったら速攻出ますが、自分にとっては切実な問題なのでメモします。
結論から言うと--spider
オプションです。
$ wget --spider ***URL.zip*** Spider mode enabled. Check if remote file exists. --2017-08-31 09:49:43-- ***URL.zip*** ... Length: 11121426 (11M) [application/zip] Remote file exists.
補足(curl)
たまにwget
入ってないよ、となる時があるのでcurl
の場合も追記します。普通にヘッダ取得しているだけです。
$ curl --head ***URL.zip*** HTTP/1.1 200 OK ... Content-Length: 11121426 ...
(単位byte
)
ちなみにファイルのダウンロード自体はcurl -O ***URL.zip***
なのですね。勉強不足です。
感想
一日の通信量上限のあるネット環境で生活しているため、迂闊に大きいファイルをダウンロードすると終わってしまう、と言う背景です。
URL
の拡張子がzip
な理由は特にないです。
辞書.appのデータ構造の補足
注意: 結論としては補足になっていません。。。RELAX NG
XMLスキーマをここで勉強した際のメモ書き程度になっています
先日書いた辞書.app
の記事ではあまり辞書を作ることに関して調べきれていなかったところがあるので追記します。先日の記事でも単語と意味を登録することはできましたが、MyDictionary.xml
の構造(辞書のデータ構造)について補足していきたいと思います。
MyDictionary.xml
の構造(辞書のデータ構造)
MyDictionary.xml
はRELAX NG
というxml schema言語で記述されています。xml
に明るくないためやや中途半端な説明になりますが、RELAX NG
は外部のデータセットを使用する前提で使われることが大半のようです(参考)
xml
なのでデータを定義するために、簡単な方法を提供してくれているそうです。例えば子要素はelement
、属性はattribute
などがあり、それらに対し1つ以上持つ<oneOrMore>
、つけてもつけなくても<optional>
、どちらか一方だけ<choice>
、などの制約をツリーとして書いていくことができます。私のイメージではタグを定義して作っていく感じです。
まず最初の方の行を見ていきます。
<?xml version="1.0" encoding="UTF-8"?> <d:dictionary xmlns="http://www.w3.org/1999/xhtml" xmlns:d="http://www.apple.com/DTDs/DictionaryService-1.0.rng"> ... </d:dictionary>
ここでは、2つのネームスペース、xml
とhttp://www.apple.com/DTDs/DictionaryService-1.0.rng
のを宣言しています。またxmlns:d
の部分で、d:
というプレフィックスを宣言しているため、データセットを使用する際は語頭にd:
をつけて使用していくことになります。(ようやくd:entry
などd:
をつけて記述していた意味がわかりました)
続いてデータセットを見てどんな定義があるのかを見ていきます。内容は/Dictionary Development Kit/documents/DictionarySchema/modules/dict-struct.rng
に記載されております。(http://www.apple.com/DTDs/DictionaryService-1.0.rng
はアクセスしても404
でちょっと分かっていません。ネームスペースとしてだけ使っているのでしょうか?)
webで公開されているものもあるので、適宜そちらも参照ください。
まず初めに<grammer>
が全体を囲っていることから、Named patterns
と呼ばれるタグを分割して定義できるようにするパターンで記述していることがわかります(リンク先の セクション4. Named patterns)。
<grammer>
は、1つの<start>
と複数の<define>
を持ちます。<define name="hoge">
と定義したものは、<ref name="hoge">
により参照することができるため、定義を組み合わせて最終的な<start>
を組み立てていく形になります。
ここで定義されている主なものを見てみましょう;
dictionary
<define name="dictionary"> <element name="dictionary" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng"> <zeroOrMore> <ref name="style"/> </zeroOrMore> <ref name="dictionary.attlist"/> <oneOrMore> <ref name="entry"/> </oneOrMore> </element> </define>
dictionary
の定義は、
- 実態は
dictionary
タグ。アップルのネームスペースを属性として指定している。(ネームスペースの必要性の理解が曖昧です。名前が一般的なので、重複しないようにという慣習的なことでしょうか?それかプレフィックスを共通化するためとか関係している?)- タグは0個以上の
style
を使用できる - タグは1個以上の
entry
を使用できる - タグは
dictionary.attlist
属性リストを使用できるdictionary.attlist
はXTHMLのバージョンと国際化指定を属性として持っているようです
- タグは0個以上の
ref
では参照先がelement
なのかattribute
なのかわからないため、どう表現したら適切か悩みます(おそらく言語で表現しにくいからこそ、この記法が使われているのでしょうが)。
entry
<define name="entry"> <element name="entry" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng"> <ref name="entry.attlist"/> <zeroOrMore> <ref name="index"/> </zeroOrMore> <ref name="Flow.model"/> </element> </define>
entry
の定義は、
- 実態は
entry
タグ。- 0個以上の
index
が使用可 Flow.model
(なんだこれ。内部にhtml
書けることと関係している?)が使用可entry.attlist
が使用可entry.attlist
はid.attrib
属性が使用可能(<index id="hoge"
とか書ける)parental-control
属性が使用可能(任意)title
属性が使用可能(任意) <- (なぜなくてもいいのだろう?)
- 0個以上の
index
<define name="index"> <element name="index" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng"> <ref name="index.attlist"/> </element> </define> <define name="index.attlist"> <attribute name="value" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng"> <text/> </attribute> <optional> <attribute name="title" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng"> <text/> </attribute> </optional> <optional> <ref name="parental-control.attrib"/> </optional> <optional> <ref name="priority.attrib"/> </optional> <optional> <attribute name="anchor" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng"> <text/> </attribute> </optional> <optional> <attribute name="yomi" ns="http://www.apple.com/DTDs/DictionaryService-1.0.rng"> <text/> </attribute> </optional> </define>
index
の定義は
- 実態は
index
タグで、属性としてvalue
、オプションの属性としてtitle
、parental-control
、priority
、anchor
、yomi
などが使用可能
といった形になっています。
つまり?
つまり今のところ、そういうデータ構造になっているということがここを読むとわかるようになったというだけのことでした。。。
これらのデータ構造が辞書データとして取り込まれた時に実際どのように扱われるかをみていく必要があり、補足し切れていないです。
データ構造の読み方はわかった、ならばタグとしてどのような形をしているか、その子要素、属性はどのように扱われるか、それらのことを次にまとめていきたいと思います。
外国語学習のためにMacの辞書.appに追加する辞書を作成する
概要
Macでは設定すると三本指タップで単語の辞書検索が行えます、文章書いている時や読んでる時に作業を中断せずに意味を調べられるのですごく便利に使わせていただいています。
最近フィリピンで生活しているので、タガログ語を読み書きすることがたまにあります。わからない単語を調べるために辞書.app
に追加する辞書ファイル(.dictionary
)が欲しかったのですが、ネットを探しても見当たりませんでした。
html
形式の辞書を見つけたので、Appleの公式ガイドライン(Dictionary Services Programming Guide)から辞書ファイルを作成してみました。その時の手順をメモしておきます。
注意: 以下の手順を行うにはApple Developer Program
への登録が必要です
Dictionary Development Kitのダウンロード
ガイドラインの指示に従い、Downloads for Apple DevelopersからAuxiliary Tools for Xcode - February 2012
を検索してダウンロードします。
ダウンロードしたdmg
を開くと、Dictionary Development Kit
ディレクトリがあるので、/Developer/Extras/
以下にコピーします。その後、その中にあるproject_template
を自分用の辞書に書き換えていきます。(テンプレートなのでコピーとかして使っていきます)
テンプレートの中身はこんな感じです。
. ├── Makefile ├── MyDictionary.css ├── MyDictionary.xml ├── MyInfo.plist └── OtherResources ├── Images │ └── dictionary.png ├── MyDictionary.xsl └── MyDictionary_prefs.html 2 directories, 7 files
バリデーションツール: jing.jar
辞書ファイルはxml
で、RELAX NG
という記法で作成します。xml
フォーマットバリデーションのためjing
を、github
から落としてきてビルドしておきます。
$ git clone git@github.com:relaxng/jing-trang.git $ cd jing-trang $ ./ant
java
がいります。jdk
インストールしてJAVA_HOME
の設定もしておきましょう。
辞書の作成
MyInfo.plist
の編集
製作者などのメタ情報を記載していきます。特に配布の予定はないのでざっと変更したくらいです。各キーの説明はCreating Dictionaries > Table 2-1を参照ください。
一旦make
まだ辞書の内容はテンプレートのままですが、完成イメージを見たいのでとりあえずmake & make install
してみます。
$ make """/Developer/Extras/Dictionary Development Kit"/bin"/build_dict.sh" "My Tagalog English Dictionary" MyDictionary.xml MyDictionary.css MyInfo.plist - Building My Tagalog English Dictionary.dictionary. - Cleaning objects directory. - Preparing dictionary template. - Preprocessing dictionary sources. - Extracting index data. - Preparing dictionary bundle. - Adding body data. - Preparing index data. - Building key_text index. - Building reference index. - Fixing dictionary property. - Copying CSS. - Copying other resources. - Finished building ./objects/My Tagalog English Dictionary.dictionary. echo "Done." Done. $ make install echo "Installing into ~/Library/Dictionaries". Installing into ~/Library/Dictionaries. mkdir -p ~/Library/Dictionaries ditto --noextattr --norsrc ./objects/"My Tagalog English Dictionary".dictionary ~/Library/Dictionaries/"My Tagalog English Dictionary".dictionary touch ~/Library/Dictionaries echo "Done." Done. echo "To test the new dictionary, try Dictionary.app." To test the new dictionary, try Dictionary.app.
辞書.app
を開き、環境設定から自分の辞書を選べるようになっているので、探してチェックを入れます。これで三本指タップで自分の辞書が表示されるようになりました。
後は辞書の内容をちゃんと書いていくだけです。
MyDictionary.xml
の編集
今回は簡単な辞書を作成していきます。タガログ語から英語の翻訳はwebからスクレイピングしました。
単語の定義ファイルは以下のようになりました
<?xml version="1.0" encoding="UTF-8"?> <!-- This is a sample dictionary source file. It can be built using Dictionary Development Kit. --> <d:dictionary xmlns="http://www.w3.org/1999/xhtml" xmlns:d="http://www.apple.com/DTDs/DictionaryService-1.0.rng"> <d:entry id="dictionary_application" d:title="Dictionary application"> <d:index d:value="Dictionary application"/> <h1>Dictionary application </h1> <p> An application to look up dictionary on Mac OS X.<br/> </p> <span class="column"> The Dictionary application first appeared in Tiger. </span> <span class="picture"> It's application icon looks like below.<br/> <img src="Images/dictionary.png" alt="Dictionary.app Icon"/> </span> </d:entry> ... <!-- 単語ごとにd:entryタグを作っていきます --> <d:entry id="一意な値" d:title="単語名"> <d:index d:value="検索キー"/> <h1>Hoge</h1> <!-- 単語のの内容、htmlかけます --> </d:entry> ... </d:entry> </d:dictionary>
html
はMyDictionary.css
でマークアップ可能ですOtherResources
ディレクトリにアセット、例えばOtherResources/hoge/image1.png
を入れておけばhoge/image1.png
で参照できます
以上です。
感想
ものすごい必要最小限のものができました。 単語間のリンクとかうまくいっていない…今度調べないと…
何よりも辞書を作る作業がとても面倒でした。著作権フリーのものを使って、.dictionary
形式で配布したほうがいいかもしれません。というか絶対どこかに「タガログ -> 英語」とか「タガログ -> 日本語」あると思うのですが、なんか見つからないです…
あとhtml
から抜いてきたのも失敗でした、他の辞書フォーマットから変換する処理を書いた方が楽だったかと思います。
iTunes ConnectにアプリをアップロードしてTestFlightでテスト
概要
iTunes ConnectにアプリをあげてTest Flightする方法がわからなかったので、多分これでいけるんじゃないかという手順をまとめました。個人的にメモしておきます。間違い、訂正ありましたら教えていただきたいです。
前提としてApple Developer Program登録済みです
当記事で使用しているXcodeのバージョンは8.3.3です、7以下のバージョンでは「3. XcodeからArchiveをアップロード」で言及しているAutomatically manage sigining機能は使えないようです
手順
0. 既存アプリのリネーム(optional)
簡単のため既存のReact Nativeアプリを再利用します。他の環境の方は無視していただいて結構です。
React Nativeアプリのリネームは簡単です。react-native-rename
をインストールし、コマンドを発行します。
$ npm install -g react-native-rename $ cd path/to/project/root $ react-native-rename 新しいアプリの名前
1. Apple Developer Portalで新しいApp IDを作成
Developper Portalにログインし、Certificates, Identifiers & Profiles
画面のサイドメニューIdentifiers
> App IDs
をクリックします。画面右上の+
を押してApp ID作成に必要な情報を入力していきます。以下は各入力項目の説明です。
- App ID Description
- name: 整理しやすいような名前をつける
- App ID Suffix (以下どちらかを選択、今回はExplicit App ID)
- Explicit App ID > Bundle ID:
reverse-domain name style string
とか一意になるように名前をつける - Wildcard App ID > Bundle ID:
com.domainname.*
のように指定すると、App IDを複数の(iTunes Connectのレコードという意味での)アプリで使用できるらしいですが、今回は使用しません。
- Explicit App ID > Bundle ID:
その他は特に変更しませんでした、お好みで設定してください。
2. iTunes Connectで新規アプリレコードを作成
iTunes Connectにログインし、マイ App
画面の左上の+
を押して新規アプリに必要な情報を入力していきます。
今回は、
- プラットフォーム: iOS
- 名前: 新しいアプリの名前
- プライマリ言語: 日本語
- バンドルID: 先ほどApp IDを作成した時のバンドルIDを選択。
- セレクトボックスには
name - bundle_id
のように表示されます - 選択肢に表示されない場合はリロードして見てください
- セレクトボックスには
- SKU: バンドルIDと同じもの
を入力しました。
3. XcodeからArchiveをアップロード
3.1. アプリの情報の編集
新しいアプリの名前.xcodeproj
ファイルをxcodeで開いてGeneral
タブへ行き項目を編集します。(react-nativeであればプロジェクトルート以下のios/新しいアプリの名前.xcodeproj
にあります)
- Identity
- Bundle Identifier: iTunes Connectで新規アプリレコード作成の際に入力した値
- Version: 0.0.1
- Build: 0.0.1
- VersionとBuildは変えなくてもいいですがせっかくなので。
- Signing
- Automatically manage sigining: チェックを入れます、プロビジョニングファイルの作成などやってくれるのでおすすめです。
- Team: 自分のチームを選択してください
以上で設定終了です。
3.2. Archiveのアップロード
画面上部、Product
> Archive
を選択しアーカイブを実行します。しばらく時間がかかる処理です。アーカイブが完了すると、ウィンドウが立ち上がります。
注意: Archive
が選択できない場合があるので、こちらを参考にしてください。
アップロードしたいArchiveを選択し、ウィンドウ右のUpload to App Store...
を押すとアップロードの準備に入ります(先にValidate...
を押してからの方が堅実だと思います、今回はやりませんでしたが)。
チームを聞かれるので自分のものを選択すると、アップロード画面になります。内容が問題なければUpload
を押してください。これも、しばらく時間がかかる処理です。アップロードが完了しビルドが作成されるとiTunes Connectからメールが来るので、気楽に待ちましょう。
ただし、失敗することもあるので、20分くらい音沙汰がなければアップロード画面の結果にエラーがないか確認しましょう。(Validate...
してからのアップロードなら、多少は安心していいかもしれません)
注意: Upload
を押した後、処理の表示がAuthenticating with the iTunes store
で止まってしまうことがあるようです。全てに対応できる方法かはわかりませんが、この辺りが参考になりました
4. iTunes Connect でTestFlightのテストを開始
4.1. TestFlightの準備
再びiTunes Connectにログインし、マイ App
で先ほど作成したアプリを選択します。タブからTestFlightを選択すると、アップロードしたビルドが表示されているかと思います。
ビルドに「輸出コンプライアンスがありません」と警告が表示されています、この状態ではまだTestFlightでテストを開始することができません。ビルドの値を押すと、画面が切り替わり、コンプライアンス情報を提出
というボタンを押すとモーダルが表示されます。今回特に暗号化していないのでいいえ
を選択しました。モーダル右下の内部テストを開始
を押すと、テストの準備が整いました。
4.2. テストへの招待
サイドメニューのテスター & グループの小項目iTunes Connect ユーザを追加
か外部テスターを追加
を選択しテストへの招待を行います。今回はiTunes Connect ユーザを追加
から自分に対して招待を行いました。
5. 終わり
TestFlightからメールが届くので指示に従い、TestFlightアプリをインストールしている端末からテストが開始できるようになります!
感想
おそらくはこれでいいだろうというものをまとめてみました。自分の環境ではダメだったなど、ご指摘いただければ幸いです。
特に使いだしてから色々いじった気がするのでXcode周りの手順がやや不安です。
FlatListのデータ更新時に再描画されない
概要
ちょっと困ってしまって、検索が時間かかったので他の人の手助けになればと思いメモします。
問題
React NavigationとFlatListでリスト編集サンプルアプリを作成した時に、FlatList
のデータ更新時に再描画されない問題に遭遇しました。
- データを
FlatList
で表示する画面がある - 他の画面でデータを更新する
FlatList
の画面を開くとデータが描画されていない(更新したものだけでなく、元からあったものの描画されない)- ちょっとスクロールすると再描画される
なんだこれ?と悩んでしまいました。
解決
検索するとextraData
を設定しろとか色々見つかったのですが、このIssueから解決できました。
コメントで言及されているケースと同じ状態だったのでFlatList
にremoveClippedSubviews={false}
を追加してあげると再描画されるようになりました。
感想
removeClippedSubviews
は高速化のためのフラグだそうです。FlatList
がラップしているVirtualizedList
のドキュメントにこのプロパティに関する言及があります。またこのページに「バグを引き起こすかも」、という注意がされているのも確認できます。
上記の方法でもうまくいかないケースがあるそうなのでIssue
を読んでみると何か糸口が見つかるかもしれません。
React Navigationで画面遷移してみる
React Nativeで画面遷移したかったのでまとめました。
内容としてはcreate-react-native-app
で作ったアプリで、2つのナビゲーター(StackNavigator
、TabNavigator
)を使ってみた勉強記事です。
注意としてはDrawerNavigator
は使わないことと、redux
等との連携も書いてないことです。
記述方法等は公式ドキュメントをご確認ください。
概要
公式ドキュメント で紹介されている画面遷移用のライブラリはいくつかありますが、 クロスプラットフォームで動作するものと考えると選択肢に入るのは以下の二つかなと思いました。 二つとも綺麗なドキュメントが用意されています。
どちらを選んでもよかったのですが、
create-react-native-app
で使ったアプリを書きたいと思ったので、
javascript
だけで書かれているReact Navigationで書くことにしました。
React Navigation
React NavigationはReact Nativeで画面遷移を実装するために、いくつかのナビゲーター(navigator)というコンポーネントを利用します。
ナビゲーターはルーティングしたり画面に引数をあげたりジェスチャーを制御したり、画面遷移に必要なものを提供してくれるものです(ざっくりです)。
標準で使えるナビゲーターとしては、StackNavigator
、TabNavigator
、DrawerNavigator
です。それぞれ以下のような特徴があります。
- StackNavigator: 画面を積んでいくタイプのナビゲーター。Screen1 -> Screen2とかScreen1 -> Screen3とか画面を積んでツリーを作っていき、行きつ戻りつしていくという感じです。描画した画面内のコンポーネントを押したら別の画面に遷移させる、といったWebのリンクと同じようなケースで使う感じです。
- TabNavigator: タブタイプのナビゲーター。[Screen1、Screen2、Screen3]のように並列に画面を依存させずに扱い、切り替えていく感じです。画面遷移はTabNavigator自身が責任を持つ感じです。
- DrawerNavigator: ドロワータイプのナビゲーター。自分的にはタブが横なら、ドロワーは縦という程度の位置が違うくらいの認識です。ただ、タブに比べて登録できる画面数が多い(多くても比較的わかりにくくならない)ので、例えばユーザーによって画面数が変化するタイプのものであれば、これを使うのかなと思います。
通常はこれらをネストさせるなどしてアプリケーションの画面遷移を作っていきます。カスタムしたナビゲーターを作ることもできるようですが、まだいいかなと思います。(丸いナビゲーターとかARっぽくてカッコ良さそう)
初心者的にはネストさせ方がちょっと不明で困ります。よく見るアプリケーションでは、
- TabNavigator(下タブ) or DrawerNavigatorをルートの親ナビゲーターとして選ぶ
- それぞれの子にScreen(普通の画面)、StackNavigator、TabNavigator(上タブ)を任意に登録
- そのそれぞれの子に画面を登録
のネストが2段くらいの構成になっていると思いますのでそれを真似します。
疑問: 認証、設定などの画面はどのように制御するのがいいのでしょうか?設定画面はモーダルを使って表示するのはよく見る気がします?認証画面もモーダルを使った表示を見ることがありますが、一瞬アプリが表示されてからシュルッと画面を乗っけるとかだと、裏側で画面が動かないように制御しなければいけない気がします。トップレベルのに認証用のナビゲーターをかませてネストを3段にするとか?
追記 2018/05/07: SwitchNavigator 使うといいかもしれないです
準備
ドキュメントを参考にインストールします。
$ create-react-native-app demo-application $ cd demo-application $ npm install --save react-navigation
StackNavigator
単純な使い方として、リストを表示して、押したらその詳細画面に遷移する例を書きます。
自動生成されるApp.js
に以下のコードを記述します。
という感じになりました。今回は単純な例なので遷移先のDetail
は使い回しです。
TabNavigator
2つの画面を持つTabNavigatorを作成します。
- App
- MathematicsList: 先ほど作った数学リスト
- AddMathItemScreen: 数学項目追加画面
やることは3つです、
- TabNavigatorのコンポーネントを作成
- 項目追加機能の実装
- 項目削除機能の実装
Step1 - TabNavigatorのコンポーネントを作成
App.js
を修正してTabNavigatorを描画するコンテナーコンポーネントを作成していきます。
... import { TabNavigator } from 'react-navigation'; /* 追加 */ /* * TabNavigatorを作成 * StackNavigatorと基本は同じ * 第二引数で画面下タブに表示されるアイコン色とラベル非表示を設定 */ const Tab = TabNavigator({ List: { screen: MathematicsList }, AddItem: { screen: AddMathItemScreen }, }, { tabBarOptions: { activeTintColor: '#037aff', inactiveTintColor: '#737373', showLabel: false, }, }); /* * 二つのタブでデータを共有するためにAppコンポーネントをコンテナコンポーネント化 * 今の所stateを持っている以外はTabNavigatorをラップしているだけ */ export default class App extends React.Component { constructor(props) { super(props); this.state = { mathematics, }; } render() { /* screenPropsで各子供にmathematicsを渡している */ return <Tab screenProps={{ mathematics: this.state.mathematics }} />; } }
TabNavigator
、StackNavigator
はコンポーネントなのでjsx
記法でrender
に書くことができます。screenProps
に値を入れると各タブでデータを受け取ることができるようです。ここでは各タブにmathematics
データを渡すために、TabNavigator
の設定をして一旦Tab
と名付けた後に、Appコンポーネントをコンテナ化し内部でscreenProps
を渡しました。
TabNavigator
で登録された画面にnavigationOptions
を設定していきます。AddMathItemScreen
は以下のように作成しました。
... import { Entypo } from '@expo/vector-icons'; ... /* * 項目追加画面を作成 * 現在はタブアイコンの設定のみのモック */ const AddMathItemScreen = () => ( <View style={styles.container}> <Text style={styles.paragraph}>This is AddMathItemScreen</Text> </View> ); AddMathItemScreen.navigationOptions = { tabBarIcon: ({ tintColor }) => <Entypo size={24} name="add-to-list" color={tintColor} />, };
tabBarIcon
のtintColor
はTabNavigator
のオプションで設定していて、active
、inactive
に応じて異なる値が送られてきます。Entypo
はcreate-react-native-app
で作成すると初めから使えるexpo
ライブラリに含まれているカスタムフォントのコンポーネントです。
AddMathItemScreen
と同様にMathematicsList
にもタブアイコンの設定を行います。
/* * StackNavigatorを変数に格納 */ const Stack = StackNavigator({ Detail: { screen: DetailScreen }, List: { screen: ListScreen }, }, { initialRouteName: 'List', }); /* * StackNavigatorをラップするコンポーネント * screenPropsにより親からもらったpropsを子にそのまま流している(手抜き) * mathematicsリストをAppコンポーネントに持たせているため、 * ここで設定したものがListScreenに渡っていく。 */ const MathematicsList = ({ screenProps }) => ( <Stack screenProps={screenProps} /> ); /* * タブアイコンの設定 */ MathematicsList.navigationOptions = { tabBarIcon: ({ tintColor }) => <Entypo size={24} name="list" color={tintColor} />, };
また、親からscreenProps
が渡されてくるのでそれをListScreen
に渡すためにここでも一旦StackNavigator
を変数に格納して、MathematicsList
内で受け渡しを行います。(もっといい方法があると思うんですが。。。ちゃんとやるときは多分reduxとか使うのでこの辺りはなあなあにしておきます)
ちなみにListScreen
は次のように値を受け取ります。
/* 引数screenPropsが親から渡ってくる */ const ListScreen = ({ navigation, screenProps }) => ( <FlatList data={screenProps.mathematics} /* mathematicsを取り出す */ ...
上記の変更をまとめたのが以下になります。
2つのタブが画面下に表示され、片方の画面は先ほどのStackNavigator
がネストされている状態になっています。
追記: タブアイコンの設定箇所をTabNavigator
の定義箇所に変更することも可能なようです。StackNavigator
に関しても同様で、タイトルなど動的に変更する必要があるかどうかで分けるという感じです。
const Tab = TabNavigator({ List: { screen: MathematicsList, navigationOptions: { tabBarIcon: ({ tintColor }) => <Entypo size={24} name="list" color={tintColor} />, }, }, AddItem: { screen: AddMathItemScreen, navigationOptions: { tabBarIcon: ({ tintColor }) => <Entypo size={24} name="add-to-list" color={tintColor} />, }, }, }, { initialRouteName: 'List', tabBarOptions: { activeTintColor: '#037aff', inactiveTintColor: '#737373', showLabel: false, }, });
Step2 - 項目追加機能の実装
項目追加機能を実装するために、App
コンテナコンポーネントからstateをいじれる関数を、TabNavigator経由でAddMathItemScreen
コンポーネントまで渡します。
export default class App extends React.Component { constructor(props) { super(props); this.state = { mathematics, }; this.addNewMathItem = this.addNewMathItem.bind(this); } /* 子供に渡す関数を作成 */ addNewMathItem({ title, detail }) { this.setState({ mathematics: [...this.state.mathematics, { title, detail }], }); } render() { /* screenPropsで各子供にmathematics、addNewMathItemを渡す */ return <Tab screenProps={{ mathematics: this.state.mathematics, addNewMathItem: this.addNewMathItem }} />; } }
受け取ったAddMathItemScreen
側でタイトル、詳細のテキスト入力、追加リクエスト処理を記述します。追加が完了したらListScreen
に遷移するようにAlert
のOK
ボタンをトリガーに設定します。見栄えに関して多少のスタイルも追加しています。
class AddMathItemScreen extends React.Component { constructor(props) { super(props); this.state = { title: '', detail: '' }; this.handleOnPress = this.handleOnPress.bind(this); } handleOnPress() { const { title, detail } = this.state; if (!title) return Alert.alert('Error', 'titleは必須です'); this.props.screenProps.addNewMathItem({ title, detail }); Alert.alert( 'Notice', '項目を追加しました!', [{ text: 'OK', onPress: () => this.props.navigation.navigate('List') }], ); this.setState({ title: '', detail: '' }); } render() { return ( <TouchableWithoutFeedback onPress={Keyboard.dismiss}> <View style={styles.container}> <View style={styles.inputGroup}> <Text style={[styles.paragraph, styles.label]}>title</Text> <TextInput blurOnSubmit onChangeText={title => this.setState({ title })} style={[styles.textInput, styles.heading]} value={this.state.title} /> </View> <View style={styles.inputGroup}> <Text style={[styles.paragraph, styles.label]}>detail</Text> <TextInput blurOnSubmit multiline onChangeText={detail => this.setState({ detail })} style={[styles.multiTextInput, styles.paragraph]} value={this.state.detail} /> </View> <Button onPress={this.handleOnPress} title={'Add item to list'} /> </View> </TouchableWithoutFeedback> ); } }
まとめると次のようになっています。
Step3 - 項目削除機能の実装
項目追加機能同様、Appコンテナコンポーネントからstateをいじれる関数を渡します。削除の方法としては、配列のインデックスをキーに各詳細画面の削除ボタンから実行します(画面遷移で操作を限定させないと容易にバグりそうですね、理詰めでバグらせられそう)
削除メソッドを追加し、screenProps
で渡します。
export default class App extends React.Component { ... removeMathItem(index) { this.setState({ mathematics: this.state.mathematics.filter((_, i) => i !== index), }); } ... render() { /* screenPropsで各子供に渡している */ return ( <Tab screenProps={{ mathematics: this.state.mathematics, addNewMathItem: this.addNewMathItem, removeMathItem: this.removeMathItem, }} /> ); }
次にDetailScreen
のヘッダー右上に削除ボタンを設置する設定を書きます。index
はListScreen
からの遷移時にnavigation.state.params
経由で渡すようにし、removeMathItem
はscreenProps
経由で渡したのでそれらから取得できます。(ListScreen
の変更は最後のまとまったもので確認できます)
DetailScreen.navigationOptions = ({ navigation, screenProps }) => ({ headerRight: ( <TouchableOpacity style={{ marginRight: 8 }} onPress={() => { Alert.alert( 'Warning', '項目を削除しますか?', [ { text: 'Delete', onPress: () => { screenProps.removeMathItem(navigation.state.params.index); navigation.goBack(); }, }, { text: 'Cancel' }, ], ); }} > <Entypo size={24} name="trash" color={'red'} /> </TouchableOpacity> ), });
以上で削除機能が実装できました。コード汚いよ、コンポーネントに切り出せよ、と思いますが指針が決まらないのでとりあえず画面単位の分割に抑えています。
完成
完成しました。こんな感じで動いています。
感想
引数の受け渡しはRedux
とかでやりたいと思いました。なのでRedux
ない状態でのプロパティの渡し方は勉強不足です。すみません。
あと書き捨てのつもりだったのでgit管理していなくて、diffだけ見せるとかいう方法もあったかもという気がします。さらによくよく考えればgistの差分でもよかったです。記事が読みにくい感じですね。
React NativeのNavigatorがなくなった
ちょっと困った
SectionList
が使いたいのでreact-nativeのバージョンをあげようと思ったら、
バージョン0.44
にはNavigator
コンポーネントがないみたいであげられなくなっちゃいました。
Navigator
はお手軽だったので、結構使ってしまっていて他のライブラリで書き直すのはちょっと面倒です。
対策
応急処置としては指示通りにreact-native-deprecated-custom-components
から使い、徐々に他のナビゲーションで書き換えていくつもりです。
感想
そういえば当時のドキュメントにNavigationExperimental
的なコンポーネントが現れたりしてて、ナビゲーションに関して試行錯誤中な匂いはあった気がします。
これを機にナビゲーションを見直すのと、githubをwatchしていこうと思います。
追記
バージョンアップして試しにimport { Navigator } from 'react-native'
してみたら、赤い画面で上の内容をディレクションしてくれますね。
動かなくなったりして迷うことはあまりないかもですね。
Reactコンポーネントをディレクトリにまとめる(ES6)
Reactのディレクトリ構成が全然わかりません。
Reactコンポーネントを分割する方法についてもメモです。ES6の文法の話だと思うのですが、正式なドキュメントが見つけられていないので間違いがあったら申し訳ないです。
書いたものをgithubに上げています。参考になれば幸いです。
webpack
を使ってビルドを行なっており、設定ファイルは以下のようになっています;
... module: { loaders: [ { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', query: { presets: ['es2015', 'stage-2', 'react'] } } ] }
ディレクトリによる分割
例として以下のようにディレクトリを構成してみました。
src/ ├── App │ ├── App.js │ ├── Bar.js │ ├── Baz.js │ ├── Foo.js │ └── index.js └── index.js
index.js
内でimport App from './App'
のようにモジュールとしてディレクトリを指定すると、ファイル./App/index.js
の内容がコンポーネントとして読み込まれます。./App/index.js
でディレクトリ内のコンポーネントを組み立てておけば、呼び出し側はあたかも一つのコンポーネントのように扱うことができます。
ディレクトリでまとめることで、意味を保ったままコンポーネントの分割が行えそうです。「components」ディレクトリ以下にコンポーネントが大量に平置きされてしまうのを防げました。
感想
一方でコンポーネントの再利用性が下がる気もします。
一部のコンポーネントは他の箇所から再利用、他はディレクトリ内のコンポーネントを利用などしてしまうと、参照がわけわからなくなってしまいます。相変わらずディレクトリ構成は悩みどころです。
ちゃんと部品単位でのコンポーネント化を意識すると多少は回避できるかもしれません。
Railsからキーがキャメルケースのjsonを返す(jbuilder)
Reactでアプリを書いていて思ったことのメモです。
大概jsのオブジェクトのプロパティはキャメルケース(camel case)ですよね。
Railsをサーバサイドにしたのですが、返却されるjsonがスネークケース(snake case)でした。
クライアントサイドで書き換えるのも面倒だと思ったので、調べてみたところRails側で対応するのが楽なようです。
内容
Rails(4から?)はjsonをリクエストした際に返却するオブジェクトの定義を、jbuilderというテンプレートエンジンで行うようです。
生成されている(例: _model.json.jbuilder)ファイルの先頭に
json.key_format! camelize: :lower ...
と記述するとキャメルケースに変更できました。
感想
ちゃんとしたapiを作るならkey_format
も引数で指定できたほうがいいのだろうか?と思いましたが使う気がしないので何もしませんでした。
クライアントからサーバへの送信の際にも書き換えが必要になると思いますが、どうしようかなーと考え中です。
json.key_format!
は単に文字列変換の関数を引数にとり、key
に適応する関数のようです。ここではcamelize: :lower
を渡していますが、他にも色々渡せますね。
この関数はそんなに使い込む機会はないと思いますが、私は関数を引数に取る関数が結構好きです。だからどうしたという話ですが。
Promise + reduceで逐次処理
setInterval
でもいいですが、Promise
とreduce
でもできるようです。
/* 逐次処理したいデータ */ const arr = [1,2,3,4,5]; arr.map( /* データの配列から`Promise`を返す関数の配列を作る */ e => ( () => new Promise( (res, _) => { setTimeout( () => { /* ここに処理を書く、ここではコンソールに出力 */ console.log(e); res(e); }, /* 処理の間隔 */ 1000 ); } ) ) ).reduce( /* reduceを使って、Promiseをthenでつなぐ */ (p, c) => p.then(c), Promise.resolve() /* 初期値 */ )
一応のメリットは、処理を中断する際の記述がわかりやすくなることだと思います。
const arr = [1,2,3,4,5]; arr.map( e => ( () => new Promise( (res, rej) => { setTimeout( () => { if(e == 4) return rej('4 is an unlucky number'); console.log(e); res(e); }, 1000 ); } ) ) ).reduce( (p, c) => p.then(c), Promise.resolve() ).catch( e => console.error('Error!!!: ' + e) )
ちゃんとする場合はsetTimeout
のコールバック関数内にtry...catch
で記述するのがいいでしょう。
setTimeout( () => { try { if(e == 4) throw new Error('4 is an unlucky number'); console.log(e); res(e); } catch(e) { rej(e); } }, 1000 );
感想
なんとなくメソッドチェーンにしたがる性分なのかもしれません。
React Native実機動作中のcommand+d
お疲れ様です。
とりあえず結論というか、言いたい事です。
React Nativeで作ったアプリを開発段階で実機にXcodeで転送した場合、
シミュレータでcommand+d
したときに出るメニューは、実機を振ると出すことができるようです。
(2017/02/22時点)
説明
最近、とりあえずiOSアプリのデモ作ってみないか?という縁がありReact Nativeを触ったりしています。
React Nativeはreact-native run-ios
とか打てばシミュレータが立ち上がるのですが、カメラを使う機能などシミュレータでできないことは、Xcode経由で実機に転送する必要があります。
シミュレータではLive Reloadという、コードを修正するたびに再読込してくれる機能を有効にできるのですが、実機ではコードを修正するたびにビルド → 転送みたいな事をしないといけないと思っていました。
というのもLive Reloadを有効にするためのメニューは、シミュレータではcommand+d
で出るのですが、実機ではショートカット打てないためです。
しかし実機に入れた場合でも、立ち上げ時にはローカル経由でjavascriptを読み込みに行っているようだし(完全にReact Native勉強不足です)。どうにかならないかなーと思い色々いじってみると、実機を振ったらメニューが出ました。その他やってみたこと
- 音量ボタン同時押し
- 二本指で画面下をスワイプ
- 三本指で画面下をスワイプ
- 二本指で画面をピンチ
- 三本指で画面をピンチ
だからどうしたという感じですね。
感想
結局実機を振るとメニューが出たのですが、振る動作を使ったアプリを作りたい場合はどうするのでしょうか?
それはそうと毎回場当たり対応で知識が集積していかない感が集積しています。
ブラウザでテキスト読み上げるコンポーネント: react-voice-components
テキスト読み上げをしてくれるReactコンポーネントの存在を知りました。grvcoelho/react-voice-components
ブラウザで音声を使って何かしたいと思っていたので、試しに触ってみて、GitHub Pageにあげてみました。 以下ちょっと苦労したので、メモです。
Reactを動かせる環境の作成
半年前くらいに作ったコンパイルの環境がかなり古くなっていたので、facebookincubator/create-react-appで作り直しました。インストールに少々時間がかかりましたが、とても便利ですね。
npm
で入れたreact-voice-components
が動かない?
npm
で入れたreact-voice-components
だと、create-react-app
で作った環境との相性なのか、コンパイルされずブラウザエラーが発生して動きませんでした。webpack
の設定ファイルを上書けばいいようですが、とりあえず、git submodule
でリポジトリをコンパイル対象のディレクトリにクローンして使用することにしました。せっかくなので、本当に問題があるのなら、いずれ修正を送ってみようかと思います。
書いたコードです;
ushumpei/play-with-react-voice-components
GitHubページを公開する
GitHubページの公開方法は3つあるようで、master
、gh-pages
ブランチのindex.html
からか、master
の/docs
を使うか、だそうです。
今回はdocs
を作成して公開しました。静的リソース(css
、js
)の指定が絶対パスだったため、読み込まれなくて困りました。
感想
GitHubのIssuesを閉じる方法
GitHubのIssuesを閉じる方法、GitHubのHelpページでも紹介されていますが、commit messageに関して書かれているだけなので、PullRequestからでも閉じられるか確認しました。
確認内容
結論から言うと、PullRequestからでも閉じられます。
以下試したことです。これらは全て、マージするとIssue n
を閉じてくれました。なお確認したのは、PullRequestの向き先がmasterの場合です。;
- commit messageの1行目に
Close #n
が記述されたcommitを持つPullRequest - commit messageの3行目に
Close #n
が記述されたcommitを持つPullRequest - titleに
Close #n
が記述されたPullRequest - descriptionに
Close #n
が記述されたPullRequest
試してみたリポジトリ(特にどうでもいい内容です)。
余談
Close
はclose
でも問題なく、この単語以外には次の単語群が使用可能らしいです。
Keywords for closing issues
The following keywords will close an issue via commit message:
close
closes
closed
fix
fixes
fixed
resolve
resolves
resolved
(転載日時: 2016/12/20)
Helpページでは、あるリポジトリから別のリポジトリのIssueを閉じる方法なども紹介されてます。
感想
PullRequestとIssueはID(#番号)の空間を共有していることに今更気がつきました。
repositoryのカタカナ表記をリポジトリとしましたが、レポジトリという発音をよく聞きます、どちらがいいのでしょう?
Yesodのquick startが重い
題に掲げた問題を解決するための記事ではないです。HaskellのwebアプリケーションフレームワークYesod
のquickstartをDockerfileで行っただけの記事です。
Dockerfile
Dockerfileを書きます。
FROM haskell:latest MAINTAINER ushumpei ARG project_name RUN stack new $project_name yesod-sqlite \ && cd $project_name \ && stack build yesod-bin cabal-install --install-ghc \ && stack build CMD ["/bin/sh"]
ビルドします。任意のプロジェクト名を引数に取れるようにARG
で指定しています。引数を渡すには--build-arg
オプションをつけて、イメージ自体に名前をつけるために-t
オプションをつけます。
docker build -t yesod_sample . --build-arg project_name=sample
ビルドできたらコンテナを立ち上げます。めちゃくちゃ時間がかかります。。。
docker run -i -t --rm -p 3000:3000 yesod_sample /bin/bash
立ち上がったらコンテナのシェルが起動します、/
以下にARG
で指定した名前のプロジェクトができているので移動し、起動コマンドを実行します。
cd /sample stack exec -- yesod devel
コンパイル、マイグレーションが終わればlocalhost:3000
で「Welcome To Yesod!」のページにアクセスできるようになります。
感想
stack build yesod-bin cabal-install --install-ghc
がめちゃくちゃ重くて面倒でした。DockerHubに重い部分がいい感じに済んでいるイメージとかありそうですね。docker
とstack
とyesod
とわからないことが二つ以上あるともうしっちゃかめっちゃかです。
Leap Motionを手に入れました
Leap Motionを手に入れました。Leap Motionは手の動きをかなりの精度で取得できるセンサーです。たくさんの言語でプログラミング可能なAPIが提供されていますが、とりあえずJavaScriptのAPIを使ってみます。また、各クラスの内容もざっくりと整理していこうと思います。
ここでは、開発PCがmacなのでSDKはV2 desktopを使います。Windowsの方は新しいSDKが使えるようなのでそちらを使ったほうがいいかもしれません。(VR関係に強化されているように見えます)
参考
- wiki
- 公式
- JavaScript SDK Documentation — Leap Motion JavaScript SDK v2.3 documentation
- github.com/leapmotion/leapjs
- Javascript | Leap Motion Developers
Leap Motion 小型モーションコントローラー 3Dモーション キャプチャー システム [並行輸入品]
- 出版社/メーカー: Leap Motion
- メディア: Personal Computers
- この商品を含むブログを見る
とりあえず
環境設定やインストールは省略します、公式サイトの指示通りにすれば問題ないはずです。
仕組みを理解するために、とりあえずブラウザ上に手を表示させてみます。まずはセンサーの値を取れるか確認をするためのコードを記述します。
ブラウザのコンソールに吐き出すだけ
<html> <head> <!-- <script src="./leap-0.6.4.js"></script> --> <script src="https://js.leapmotion.com/leap-0.6.4.js"></script> <script> Leap.loop(frame => console.log(frame)); </script> </head> <body> </body> </html>
LeapJSを外部リソースとして取得します。Leap
はLeapJSがネームスペースとして確保しているオブジェクトのようです。
値をとるにはLeap.loop
にコールバック関数を渡します。この関数にセンサーが取得したframe
オブジェクトが引数で渡されるので、それに対する処理を記述するのがアプリケーション作成の基本的な流れになると思います。
frame
オブジェクトの詳細はリファレンスに書いてあります。手、指、ツール(棒状のもの)などのデータをプロパティとして持っていてくれています。
プラグイン Rigged Hand
を使う
LeapJSはプラグインという形で機能拡張できるようになっています。プラグインはController
クラスによって管理されます。各プラグインはplugin
メソッドにより登録しuse
メソッドにより有効にしたり、use
メソッドに直接関数を渡すことで、loop
実行時にコールバックとして呼び出されるようになります。
プラグインを使うための準備として、まずは先ほどの処理をLeap.loop
を使わない記述方法に変えておきます。
<html> <head> <!-- <script src="./leap-0.6.4.js"></script> --> <script src="https://js.leapmotion.com/leap-0.6.4.js"></script> <script> const controller = new Leap.Controller(); controller.use( () => ({ frame: frame => console.log(frame), }) ); controller.connect(); </script> </head> <body> </body> </html>
プラグインを追加する準備が整ったところで、リアルな手を表示することができるRigged Hand
というプラグインを追加してみます。
プラグインを使用するにはleap.rigged-hand-0.1.7.js
の他に、いくつかのライブラリを読み込む必要があります。use
メソッドでriggedHand
を使用するようにします。
<html> <head> <!-- 以下から必要なライブラリを取得 https://cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.js https://js.leapmotion.com/leap-0.6.4.js https://js.leapmotion.com/leap-plugins-0.1.10.js https://github.com/leapmotion/leapjs-rigged-hand/blob/master/build/leap.rigged-hand-0.1.7.js --> <script type="text/javascript" src="three.js"></script> <script type="text/javascript" src="leap-0.6.4.js"></script> <script type="text/javascript" src="leap-plugins-0.1.10.js"></script> <script type="text/javascript" src="leap.rigged-hand-0.1.7.js"></script> <script> const controller = new Leap.Controller(); controller.use('riggedHand'); controller.connect(); </script> </head> <body> </body> </html>
動作はこんな感じになりました。実際の手も一緒に撮影すればよかったです。。。
Leap Motion - Rigged Hand Demo
LeapJSのクラスに関する考察
API Referenceによると、LeapJSには次のクラスが存在します。
制御
クラス名 | 概要 |
---|---|
Controller | Leap MotionのAPIに接続するためのインターフェース。オプションや、フレーム更新時のコールバックの設定など、最もさわりそうなオブジェクト |
InteractionBox | Leap Motion Controllerに紐づいた表示領域を扱うオブジェクト?領域に応じたベクトルの正規化方法などを提供してくれるみたいです |
Frame | センサーがフレーム更新ごとに取得した手、指に関するデータを含むオブジェクト |
パーツ
クラス名 | 概要 |
---|---|
Pointable | 指(Finger)やツールなどの抽象クラスで位置や方向、速度などを保持しているオブジェクトです |
Hand | 手のオブジェクトで、5本の指の配列や腕などプロパティに持っています。かなり豊富な情報を持っているようです |
Finger | 指のオブジェクトで、生えている位置や、手の骨の本数文の骨オブジェクトに関する情報を持っています。何指か、などの識別子も持っています |
Bone | 骨のオブジェクトで、指の骨を表しています。前後の関節を持っています |
ジェスチャー
ジェスチャーは多いので個々の概要は省略します。認識してくれるハンドサインという認識です。独自に追加できるかはちょっとわからないです。
- Gesture
- CircleGesture
- ScreenTapGesture
- KeyTapGesture
- SwipeGesture
計算要素
クラス名 | 概要 |
---|---|
Matrix math | 行列のオブジェクト。WebGL用外部ライブラリを使っているようです |
Vector math | ベクトルのオブジェクト。WebGL用外部ライブラリに入っているvec3を使っているようです |
感想
API Referenceを読んで、勝手に各クラスの概要などを書きましたが、このドキュメントを和訳した方が随分人のためになるんだろうな、と思いました。(もしかしたら和訳されている?)
まだまだわかっていないことが多いので、ご指摘いただければ幸いです。