技術メモ

Scala,React Nativaなどの技術メモ

Parse終了のお知らせ。でもやってみる。

Parseサービス終了するらしい

blog.parse.com

UI好きだったので残念だ。いまReact Nativeで作っているアプリのために使う予定だったのだけど。 他のサービスや自分でサーバサイドを一から書くことを検討もしたけど、やっぱりこのままParse使ってみようと思う。

理由

  • 終了まであと1年くらいある
  • Parse Server(上記記事参照)などもでたので、こっちも使ってみたい
  • まだParseというサービスの良し悪しを体感できるほど使えていない
  • 移行作業もそれはそれでいい機会

Parseの特徴

  • UserやRoleごとに細かいアクセス制御ができる
  • 多対多の関連を表現するための関連テーブル(Parseでいうclass)はいらない

ParseReactについて

github.com

Seamlessly bringing Parse data into your React applications.

とあるので、ReactからParseを使うための実装かと。

Parse Javascript SDK との違い

READMEによると

Parse + React is an interface layer on top of the Parse JS SDK that provides simple access to the Parse API from React.

とあるので、Parse Javascript SDKをReactでラップしてくれているぽい。 ブラウザなどでのJSからの用途ではParse JS SDKをReact NativeからはParseReactを使う感じなんだと思う。 ReactNativeは、JSの実行環境が特殊だったりするので、そのための対応などだろうか。

今後の調べるべきこと
  • ブラウザ(React)とReactNativeの実行環境の違い

実装されたAPI

github.com

データの取得やバインドのような機能を提供するAPIも独自に追加されている。 このobserveは確か多対多関連の参照でうまく動かなかった記憶があるので、後ほど検証する。

ParseNativeでParseを使う

一対多関連の表現

AuthorBookを例にあげる。

一対多関連の選択肢
  • Pointers
  • Arrays

今回はPointersを使ってみる。

下記リンクによるとParse Relationsによる関連付けはwebからはできないらしいので、ParseダッシュボードのAPIコンソールから、実際の関連付けを行う。

www.parse.com

エンドポイントは、

PUT classes/Book/AAA

で、これに対して、

{
   "author":{
      "__type":"Pointer",
      "className":"Author",
      "objectId":"BBB"
   }
}

のようにauthorカラムにidがBBBAuthorPointerとして紐付けている。カラムは自動で生成される。 これをReact Nativeから取得するには、

observe: function (props, state) {
   return {
      user: ParseReact.currentUser,
      books: new Parse.Query('Book').include('author')
   };
},

とすると、

{
   id: { className: 'Book', objectId: 'AAA' },
   className: 'Book',
   objectId: 'AAA',
   createdAt: ***,
   updatedAt: ,***
   ACL: ***,
   author: {
      id: { className: 'Author', objectId: 'BBB' },
      className: 'Author',
      objectId: 'BBB',
      createdAt: ***,
      updatedAt: ***,
      name: 'name'
     }
}

のようにあるデータが取得でき、books.author.nameにアクセスできる。 includeについてはParseのREST API Guide

Use on Pointer columns to return the full object

とあったので、Pointer型専用ぽい。JS SDKのDocsには特にそんな記述ない感じだけど、APIレベルでPointer型専用ならそうなんでしょう。

多対多関連の表現

BookAuthorの多対多の関連を例にあげる。

parse.com

上記ドキュメントによれば、次の3つの選択肢があるようだ。

多対多関連の選択肢
  • Arrays
  • Parse Relations
  • Join Table

今回は、多対多関連の片方のオブジェクト数(Book)が100以上になりそうなこと、 BookAuthorに限れば、関連の作成日時などのメタデータは必要ないことからParse Relationsを選択する。

Arraysの選択時は、2016/02/04現在Pointer型の配列展開に関するバグが報告されているようなので注意が必要だ。

github.com

Parse Relationによる多対多の関連付け

エンドポイントは、

PUT classes/Book/AAA

で、これに対してQuery Parameter

{
   "authors":{
      "__op":"AddRelation",
      "objects":[{
         "__type":"Pointer",
         "className":"Author",
         "objectId":"BBB"
      }]
   }
}

でリクエストする。 これはidがAAAのBookクラスのauthorsカラムに、idがBBBのAuthorクラスをRelation型として関連付けることを表す。 カラムがなければ自動で生成される。

関連を含むデータを取得

ParseReactのobserveを使う
observe: function (props, state) {
   return {
      user: ParseReact.currentUser,
      books: new Parse.Query('Book')
   };
}

observeを使って、Bookデータを取得する。

{ 
   id: { className: 'Book', objectId: 'AAA' },
   className: 'Book',
   objectId: 'AAA',
   createdAt: ***,
   updatedAt: ***,
   ACL: ***,
   authors: {
      parent: { _objCount: 9, className: 'Book', id: 'AAA' },
      key: 'author',
      targetClassName: 'Author'
   },
}

取得できたBookのあるデータを示す。 関連は持ってるぽいが、Authorのnameプロパティは直接取れていない。

new Parse.Query('Book').include('authors')

とかしたら、データが取得できなくなることからも、includeはやはりPointer型専用か。

authors: {
      parent: { _objCount: 9, className: 'Book', id: 'AAA' },
      key: 'author',
      targetClassName: 'Author'
   }

これだけの情報じゃ、Authorテーブルからも引けないけどどうするんだ。

matchesQueryを使う
var query = new Parse.Query(Parse.Object.extend("Book"));
var innerQuery = new Parse.Query(Parse.Object.extend("Author"));
query.matchesQuery("authors", innerQuery);
query.get("AAA", function(book) {
      console.log(subjectObj);
});
{ className: 'Book', _objCount: 12, id: 'AAA' }

得られたのは、BookクラスのParse.Objectっぽいが、

get('authors')
{
   parent: { _objCount: 12, className: 'Book', id: 'AAA' },
   key: 'authors',
   targetClassName: 'Author'
}

authorsプロパティは取得できない。

innerQuery.exists('not_exists_property');
null

となることからも、matchesQueryBookに対してもAuthorに対してもクエリをかけてくれるようなものではなく、 ある条件を満たすクエリをBookにかけるような挙動であると予測できる。

Parse.Relationを使う
new Parse.Query('Book').get('AAA').then(function (book) {
   book.relation("authors").query().find().then(function (authors) {
      console.log(authors);
   });
});
[ { _objCount: 11, className: 'Author', id: 'BBB' }, { _objCount: 12, className: 'Author', id: 'CCC' } ]

このような出力が得られるが、一見するとnameプロパティは含まれていないよう。 しかし、Parse.Objectからは.getメソッドでプロパティを取得できる。

 authors.map(function (x) {
    return x.get('name');
 })

まとめ

多対多の関連の参照

Parse.Relationを使った多対多関連を一つのクエリで参照することはできないらしい。 しかしParse.Relation使えば、複合的な記述にはなるが十分に使える。

observeについても、うまく複合クエリをセットできればちゃんと使えるのかも。

今後に生かすこと

JSのobjectと、Parse.Objectの区別がつかず、getメソッドによるプロパティ取得に気付けなかったことが時間がかかった原因になった。typeofconsole.logからはその違いが見つけづらかった。

それと英語をもっと速く読めるようになりたい。急がば回れだ。

スクレイピングで画像収集

目的

GoogleTensorFlowや、Cloud Vision APIを公開してるみたい。これらを使って、画像中から(できればリアルタイムで)物体検出をしてみたい。そのための学習用の画像を集める。

技術選定

スクレイピング系だとPythonの情報が多い。自分もそんなイメージあったけど、なんで?そういうライブラリ充実してんのかな。

qiita.com

この辺りを参考にしていく。

環境構築

qiita.com

pipはピップと読むらしい。この辺を参考にしてみるか。

blog.amedama.jp

後方互換性ないのか。Python2を使う理由は今のところない。

zipimport.ZipImportError: can't decompress data; zlib not available

xcode-select --installで回避。

クローリングとスクレイピング

http://momijiame.tumblr.com/post/114227737756/python-beautifulsoup4-を使って-web-サイトをスクレイピングする
momijiame.tumblr.com

どうやら二つの意味は違うみたい。ウェブスクレイピングは厳密には各ページを順番に辿っていくクローリングとページから情報を抽出するスクレイピングの二つのパートに分かれている。 まあ、通りでPython スクレイピングだと、ページ送りに関する情報が出てこないなと思った。

http://momijiame.tumblr.com/post/114579225706/python-scrapy-と-beautifulsoup4-を使った快適-web-スクレイピング
momijiame.tumblr.com

matchとsearch

BeautifulSoup4でスクレイピングしていくのだけど、細かなデータの取得には正規表現を使いたい。 matchは先頭からの一致をみる。searchは途中でも構わず一致をみるという違いがあるぽい。 matchで取れなくて焦った。

7.2. re — 正規表現操作 — Python 2.7.x ドキュメント

Pythonにおける文字コード

なんかPythonこんなに文字コード意識しないといけないとは知らなかった。。。

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe3 in position

などというエラーが。スタックトレースから、"".joinあたりでエラーが出ていること、 配列を使わないときはエラーが出ていないことなどから、配列周りが怪しい。

配列をstrに代入しようとしていたことが、原因ぽかった。もう少しわかりやすいエラー出してくれないかな。

画像を保存

image pipelineを使う

Downloading and processing files and images — Scrapy 1.0.4 documentation

exceptions.ImportError: No module named PIL

って怒られるから、

pip install PIL
You are using pip version 7.1.2, however version 8.0.2 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

って怒られるから、

pip install --upgrade pip
pip install PIL
Could not find a version that satisfies the requirement PIL (from versions: )
No matching distribution found for PIL

って怒られるから、

brew install pil
>Instead of PIL, consider `pip install pillow` or `brew install Homebrew/python/pillow`.
pip install pillow

image_urlsへの渡し方は リストかつUnicodeじゃダメっぽい。

image_urls = [img['src']]

画像名も変えられるぽいが面倒くさそうなのでやめておこう。

Python所感

文字コード周りが慣れないので難しい。 また、エラーコードからのデバッグがなかなか難しい。 重要な情報を吐いてくれない気がする。

react native social login 続き

react-native-social-login導入

github.com

exsampleを諦めて、とりあえずサンプル実装したら動いた。なんだったんだ。。 node_modulesの削除と、npm installやり直し、xcodeの再起動は効いてそうだった。

iOSプロジェクト側の設定

developers.facebook.com

facebook developersにログインしてないと見れないけど、この辺りを参考にinfo.plistをひたすらいじる感じ。iOS9からは設定が異なるので注意。

facebook developers側の設定

f:id:papuaaaaaaa:20160109213834p:plain

ボタンを押せども押せども上記のような画像が出る。iOS9ではnativeアプリではなくsafariに飛ばされるが、後で触れる。最初はiOSプロジェクト側の設定がミスってるのかと思ってたけど、原因はfacebook developersでの設定不備だった。

設定>Basicの画面でwebプラットフォームしか登録していなかったことが原因。

f:id:papuaaaaaaa:20160109214055p:plain

Add PlatformのボタンからiOSを追加すればBundle Identifierなどを登録できるようになる。

iOS9めんどくさい

FBLoginManager.LoginBehaviors.Nativeを指定しても、nativeアプリではなくsafariが開く。調べるとちゃんとissueが立ってる。

github.com

この辺にiOS9とそれ以前の違いについても詳しく書いてある。アプリ間遷移の仕様が変わったようだ。

developers.facebook.com

サポートサイトでも議論になっている。

developers.facebook.com

stackoverflow.com

なんかFacebookの中の人が「safariで多くの人がfacebook使ってるから、safari経由でoauthしたほうが便利」とか言ってるっように読める。 nativeアプリはの僕としてはうーん。。

自分で書く?

react-native-facebook-login見てみると、ネイディブ機能を操作するコードをObjective-Cで書いていて、それをreact-nativeでラップしてる。Objective-Cで書けばnativeアプリ呼べると思うけど。。

今ここで頑張るのはやめておこう。safari view controllerひとまず使っておいて、内部実装から進める。

さてさて、'FBSDKCoreKit/FBSDKCoreKit.h' file not foundについて、根本原因を探っていく。

xcodeにおけるframeworkとは

developer.apple.com

ただ単に、Build PhasesLink Binary With LibrariesFBSDKCoreKit.frameworkが追加されていなのが原因かな?と思ったが追加しても変わらず。

あっ、なるほど。Build SettingsSearch PathsFramework Search Pathsにframeworkへのパスを追加したらいけた。 ちゃんと読み込めるプロジェクトとframeworkへのパスが違うのにどうやって指定しているんだろうと思ったとこからの解決。

RTCRootView.h not found

ちゃんとnpm installしててるのに出てる場合、xcodeを開き直す。

stackoverflow.com

Command /bin/sh failed with exit code 1

キタコレ。前も悩まされたな。だがこれの解決方法は習得済みさ。

papuaaaaaaa.hatenablog.com

この辺にも書いてたけど、ちゃんと他のエラーメッセージ読めっていいう当たり前の話だった。

uncaught error Error: UnableToResolveErrorInvalid directory /Users/node_modules/NativeModulesそんなディレクトリないよって話ですね。

うん、解決できず。

project設定

projectじゃなくて、targetのframework pathに設定。

react native social login

react nativeシリーズは備忘録的にたいした編集もせずに書く殴っていたが、いがいに見てくれているひとがいるらしく申し訳なく思った。しかし、いまは編集の時間がもったいないのでこのままリリースまでは走る。

parseの設定

さてさて、ビューができてきたのでそろそろ画像などちゃんと表示させたくなる。ここまでくるとダミーデータをつくるのも非効率な気がしてきたので、サーバサイドから値を返すようにする。

サーバサイドには、MBaasのparseを選定した。理由は時間的に低コストでできそうだからMBaas。MBaasのなかでも評判が一番良さそうだった。これまでMBaasはKiiCloudくらいしかまともに使ったことがない。

dashbordからリレーションの設定

webのコンソールからいくつかダミーデータをつくっていく。最初普通にuserIdとかってカラムをつくって参照してたが、よくよくみるとRelationって型を選択できる。webのコンソールからリレーションをはることはできないらしい。

www.parse.com

と、思ったけどよくよくみるとこれは2年も前の記事で、実際にparseのdashboardで試してみるとRelation型のカラムのView relationボタンから参照一覧に飛んで、そこで+ボタンから追加できそう。

API経由でリレーションの設定

parse.com

このあたりを参考にすると、API経由でリレーションの設定もできる。

columnNameのとこにカラム名を指定するの注意。

f:id:papuaaaaaaa:20160107231827p:plain

また、はまりポイントとしては、カスタムクラスのエンドポイントはAPI Consoleにウォーターマークでもでるように、classes/_Userみたいな感じでもいいんだけど、デフォルトであるUserクラスのエンドポイントが間違っていてハマった。

parse.com

ま、リファレンスにあるとおりなんだけど、/usersになるみたい。parseのUI好きだなあ。

SocialLoginの実装

ライブラリの選定

さてダミーデータ準備できたが、データをUserに紐付けたので当然ながら認証の問題がでてくる。めんどくさいとは思いながらも、いい感じのライブラリないかなと思ってたら、react-native系のライブラリまとめを発見した。スターの数はやっぱり参考になる。

github.com

social login系ではreact-native-loginあたりがいいなとおもったけど、Warning: This project is running on React Native 0.5.0, which is a couple of months old. Pull requests welcome to update to the most recent version!とのこと。0.5.0はちょっと古すぎるなあ。いま0.17.0つかってるし。Pull requestsの期待に応えられないことに歯がゆさを感じつつ、他をさがす。

github.com

とまあ、react-native-facebook-loginなるものもよさそう。package.jsonみる感じ0.14.0なのでまあまあ期待できる。なのでこれを導入してみる。

github.com

react-native-facebook-loginの導入

FacebookSDKは個別に落とす必要はなく、ライブラリに含まれている。

QuickStartに従って設定を進める。(facebook for developersにログインしたら見られる)

Xcodeのビルドが通ったので設定はできた模様。とりあえずexsampleにあるサンプルを動かしてみようと思ったが動かず。

RCTRootView.h' file not found

github.com

違うライブラリのissueだが、これを参考にRCTRootView.hを追加してあげたら解決できた。

FBSDKCoreKit.h' file not found

stackoverflow.com

またなにかしらでてくるが、このあたりを参考にnpm installしてみる。いちどした気がしたけどな。

example.xcodeproj Applications using launch screen files and targeting iOS 7.1 and earlier need to also include a launch image in an asset catalog.

こいつはとりあえず対象OSバージョンを9.2とかにして消して、

sourcecode.c.h in Frameworks & Libraries build phase

stackoverflow.com

こいつは、どうやらさっきLink Binary with Libraries.hファイルを追加したことがよくないらしく、それらを消す。

ld: warning: Auto-Linking supplied '../node_modules/react-native-facebook-login/FacebookSDK/FBSDKCoreKit.framework/FBSDKCoreKit', framework linker option at ../node_modules/react-native-facebook-login/FacebookSDK/FBSDKCoreKit.framework/FBSDKCoreKit is not a dylib

example/Frameworksに残っていた.hファイルも削除する

FBSDKCoreKit.h' file not found

と、ここにまた戻ってきた。。。

反省

どう考えてもiOSアプリのプロジェクト構成やビルド、リンク周りの仕組みを理解せずにやってみてるから問題が解決できないわけ。ヘッダファイルが見えてないのだから、原因は明白なわけで。ちょっと一旦仕切りなおそう。

React Nativeでビューのコーディング

#前回まで

github.com

react-native-scrollable-tab-viewを導入するところまでできた。各タブのviewを作りこんでいく。

 

#tabのviewをつくる

たぶの画面をトップレベルのviewで埋めたいのだけどなかなかできない。異なる背景色をつけて、ひたすら広がりを確認する。しかし、childの範囲しか広がらない。flexmxml書いてたときもコレ同じことやってた記憶がよみがえる。

f:id:papuaaaaaaa:20160103153343p:plain

そうそうこんな感じにしかならない。


moduscreate.com

このあたりを参考にしながら。この記事自体は学べること多かったけど、react-native-scrollable-tab-viewと一緒に使うとなかなか思い通りにならない。`flex: 1`にすればいいんじゃないのかよ、っとおもって、サンプルごっそり移植するもやっぱり同じなので、これ原因は別のところだと思って調べてみると、本家ドキュメントで原因がわかった。なるほど。

 

`Keep in mind that ScrollViews must have a bounded height in order to work`

 

#ScrollViewのstickyHeaderIndicesの挙動

なんかおかしいなー。固定されてくれないし。

#requireについて

`var { Icon, } = require('react-native-icons');`

 

 

`var Icon = require('react-native-icons');`

 

の違いについて。

 

#ScrollableTabViewのchildsの最大の大きさに他も影響を受けてしまいそう

ScrollViewでないタブもスクロールバーが出現してしまう現象が発生。ScrollableTabViewの直下にheightが大きな子がいたらそのheightの影響をうけているような挙動。

 

Viewでラップして、Viewのheightを統一した。ListViewをラップした箇所で、Warningが発生。

 

`Warning: ScrollView doesn't take rejection well - scrolls anyway`

関連してそうなissueがこちら。まだ対策はなさそう。一旦無視する。

 

github.com

 

#タップで別Viewを出す

`TouchableHighlight`と`Navigator`をつかえばいいかな。`TouchableHighlight`のmouse_down時のいろは`underlayColor`で変えられる。

 

onPressなどで、Navigatorの参照をどう得るかについて悩んだが、とりあえずrefをつかってしまった。あまりよくなさそうではある。

 

次のsceneが下から浮かび上がるようにしたい。

`Navigator.SceneConfigs.FadeAndroid`でいけた。

これネーミングどうなの。。。

 

f:id:papuaaaaaaa:20160104121211p:plain

 

TabBarのなかで、別sceneが開いてしまう。あたりまえか。これTabBarまで被せるにはModalつかうしかないか?

 

issueにもあがってた。

 

github.com

 

#cssで円をつくる

stackoverflow.com

 

#`flex: 1`なviewの子をabsolute配置

 

f:id:papuaaaaaaa:20160104133618p:plain

 

```

wrapper: {
flex: 1,
backgroundColor: '#FF0000',
},
bar: {
position: 'absolute',
flexDirection: 'row',
bottom: 0,
right: 0,
left: 0,
height: 40,
backgroundColor: '#000',
},

```

stackoverflow.com

NavigatorIOSからの脱却2

最新ReactNativeでビルドまで

papuaaaaaaa.hatenablog.com

環境ぽい原因で一度失敗したので、react-native入れなおすところから。 nodeのバージョンもあげる。

npm install -g react-native-cli あたらしいcliが入っていればreact-native initしたときに新しいバージョンを入れてくれるぽい。

空のプロジェクトのビルドが通らない。 Command /bin/sh failed with exit code 1

qiita.com

qiita.com

このあたりのパス通ってない話かと思って、zshじゃなくてbash使ってるからか?なんて思って、 Build Phases > Bundle React Native code and iamagesのShellの設定をいじったりもしてたけど、 xcodeのエラーログを読んだら、const絡みのSyntax Errorだったのでnodeが原因かな。

別な原因なのにこのエラーがでていることもありそう。エラー前まで戻って、ビルドせずにエミュレータのリロードで 変更を確認できるように開発を進めると、正しいエラーメッセージが得られることもあった。

react-native-scrollable-tab-view/FacebookTabBar.js at master · brentvatne/react-native-scrollable-tab-view · GitHub にてreact-native-iconsライブラリをリンクしていないことが原因など。

nvmでいまいちちゃんとnodeのバージョンちゃんと固定されないなと思っていたところ。 nvm alias default v5.3.でビルド通るところまでいけた。

なんか気付かなかったけど、react nativeのサーバも裏で自動で走るようになってる。 いままでいつからか手動でしてたぽい。

react-native-scrollable-tab-viewを導入する

github.com

READMEのサンプルコードが少しおかしかったので修正しといた。

github.com

いい感じにいけた。アイコンの下にラベルをいれるためには、本家のソースをいじる必要がありそう。 タブをまたいでジャンプしたときに、アイコンの色が灰色に戻りきらないバグがありそうな感じ。 ソースコードはとても参考になりそう。

アイコンの色はissueにあがってた。

github.com

react-native UIExplorerのはまり

開発しながらUIExplorerでサンプルを確認したいので、ローカルでserverを2つ動かしたいんだけど、どうもポートが被る。 AppDelegate.mのポート番号書き換えるだけで行けた気がしたんだけど、npm startするたびに8081でしか起動してくれない。

RCTWebSocketExecutor.mやpackager/package.jsonの./packager.shに--port=8080のようなオプションを書いたりしてもだめ。 仕方ないのでserverをbundleすることにする。AppDelegate.mにも説明が書いてあるのだけど、

  1. ローカルでnpm start
  2. curl でjsbundleつくって
  3. AppDelegate.mでOption2を使うようにコメントアウトを編集する
  4. それをプロジェクトに読み込ませる

の手順でできる。 このあたりが参考になった。 PCと繋ぐ必要がないので便利。これは正解だった。

stackoverflow.com

まとめ

ということで、NavigatorIOSからの脱却には成功したぽい。