kkkdev’s 開発 備忘録

残しておきたい開発情報を記録します。

API BluePrintとSwaggerのドキュメント生成を同時に行う

それそれに利点がある

Web APIのドキュメント記述の言語仕様であるAPI BluePrintとSwaggerですが、 それぞれのドキュメント生成ツールが存在します。

API BluePrint aglio(npm)にて、シンプルなAPIリファレンスを生成可能です。

Swagger Swagger UIにて、リファレンス上で動的にAPIを実行可能な インタラクティブなドキュメントを作成可能です。

両方のドキュメントを作成する手順(一例)

**1.Swagger UIのHTMLをダウンロードしておきます。 (公式ページからも落とせますし、npmにもswagger-uiという名前で存在します)

2. API BluePrintを書く

3. Swaggerに変換する

apib2swaggerという、名前通りAPI BluePrintをswaggerのjsonに変換するコマンドがnpmにあります。

apib2swagger -i someApi.apib -o someApi.json

4. 諸々アップロード後、swagger UIを (swagger jsonを指定して)開く

swagger ui一式 / 生成されたjsonをアップの後、swagger UIのページをhttpで開きます。 この際に、下記のようにswagger jsonのURLを引数で指定することにより、 自身の仕様を読み込んでくれます。

http://(swagger uiの設置場所)/dist/?url=(swagger jsonのURL)

なお、下記のソースを見ると分かるように、引数が無い場合は ハードコーディングされたURLを読み込みます。 (こちらを書き換えてしまうというのもアリ)

[dist/index.html]
———(略)———
  <script type="text/javascript">
    $(function () {
      var url = window.location.search.match(/url=([^&]+)/);
      if (url && url.length > 1) {
        url = decodeURIComponent(url[1]);
      } else {
        url = "http://petstore.swagger.io/v2/swagger.json";
      }
———(略)———

以上です。

BluePrintだけ書けばよいので、自動化してしまえば非常に楽に 軽量(aglio)&インタラクティブドキュメント(swagger UI)が作成可能です!

Appiumでの要素名の探し方

iOS / Android で指定方法が異なる ・OSのバージョンで部品の位置が変わる ・機種ごとで部品が変わる

と、割と追求し出すと泥沼。 どの機種でもそれなりに動く着地点を探すしかない。

iOSは、iPhone6以前と以後で判定を分ける。 Androidは部品単位(TextView等)の何番目とかで探すのがよさそう。(リソースIDが鉄板と思われたが、全く表示されない機種が存在する)

iOS http://qiita.com/edo_m18/items/3af59b10e2f1f1775910

Android http://qiita.com/kkakizaki/items/e46307257b78d22bd74c

バーコードを読み取ってカロリーを検索するアプリ(のソースコード)を公開

CalorieScanner_Client(iOSアプリ) github.com

CalorieScanner_Server(APIエンドポイント) github.com

クライアント(iOS)・サーバー(PHP)共に上記に公開しました。

概要

カメラで食品のバーコードをスキャンすると、カロリー情報を検索して候補を表示します。 Yahoo 商品検索API 他を利用しています。 *上記のAPIキー/アクセストークン等は、自身で発行・設定頂く必要があります。

手順

事前:APIエンドポイント・APIキー諸々の設定


手順1.「スキャン」タブでバーコードを読み取ります。(連続読み取り可)

f:id:kkkdev:20160822165256p:plain


手順2.「結果」タブを選択すると、APIに問い合わせが始まり、結果(商品候補)が返却されます。 (候補が複数ある場合は、infoマークが表示されます)

f:id:kkkdev:20160822165258p:plain


手順3.infoマークを押すと、候補から1つ選択ができます。

f:id:kkkdev:20160822165300p:plain

キーワード

Swift:RxSwift / SVProgressHUD / Alamofire / モナド(Optional)
PHP:Composer / Guzzle / Slim framework / レーベンシュタイン距離

ふりかえり

手元に買い込んだカロリーメイトフィットチーネグミは大丈夫でしたが、 やはり検索結果が存在しないものも多いです。

上記みたいなメジャーなお菓子は引っかかりやすいかも?

実際のエンドポイントもありますが、オープンには非公開です。 (エンドポイントを利用した場合、基本的には1時間で120回までの問い合わせ制限回数となります。 結果が返らなかった場合は時間をおいて再度アクセスしてみてください。 )

しかし、RxSwiftを利用しましたが、こういうアプリでバインディングを使うと 本当ラクでした。(データのことだけ考えればよい)

ソースの公開自体の経験がないので、(いろんな面で)どんどんブラッシュアップしていきたく思う所存です。 TravisとかTravisとか... (Fitbit API利用も、アプリとしてプラットフォームで公開する際は 利用者ごとにOAuth認証する必要があります)

Pimple + yaml2pimple で依存性のコードからの分離をシンプルに実現

PimpleでDIを行うにしても

$container['key'] = function ($c) {...

みたいな初期化は自分で記述する必要があるのですが、 yaml2pimpleを使うと、
Symfony/DependencyInjection のように yamlから自動的にインスタンス生成/依存性の注入を行ってくれます。

github.com

導入前

  $container['apiAdapter'] = function ($c) {
      return new APIAdapter();
  };

  $container['api'] = function ($c) {
      return new TrueAPI($c['apiAdapter']);
  };

導入後

[PHP]

$container = new Container();
//yamlから依存性を読み込む
$builder = new ContainerBuilder($container);
$locator = new FileLocator(__DIR__);
$loader = new YamlFileLoader($builder, $locator);
$loader->load(__DIR__ . '/services.yml');

[yaml]

parameters:
  name: aaa # 実際使わなくても、必要がある

services:
  apiAdapter:
    class:   APIAdapter
    arguments:  [@api]
  api:
    class:   FakeAPI

yamlを書き換えるだけで依存を変更できるようになりました。

nesbot.com

上記ページのようにSlim frameworkのController初期化を遅延させたりするのにも使えそうです。

https://github.com/gonzalo123/yml2pimple/commits/master
factory等のメソッド対応もマージ済のようです。

サンプルコード

github.com 前述の内容のサンプルです。(composer install → test.phpを実行)

swiftのOptional + flatMapで複雑なjsonをパースする

swiftの最大の利点といえばOptional型だと思いますが、 扱い方を間違えるとネストやif文がすぐに大量生産されてしまいます。

今回は、なるべくシンプルな記述で複雑なデータ構造を扱うことを検討してみます。

{
    "foo": {
        "bar": [{
            "baz": {
                "value": "aaaa"
            }
        }, {
            "baz": {
                "value": "bbbb"
            }
        }, {
            "baz": {
                "val": "cccc"
            }
        }, {
            "baz": {
                "value": null
            }
        }]
    }
}
  • 原則として、[foo] [bar]の中に[baz][value]の配列がある
  • それ以外のデータは不正なフォーマットと見なして無視する

こんな感じの(httpで取得した)jsonをライブラリ無しでパースして、 いくつかの方法でvalue の値を取り出してみます。

jsonを取得し、NSDictionaryのOptionalに変換

var data:NSData? = NSURL(string:"(jsonを取得するURL)").flatMap{NSData(contentsOfURL: $0)}
var jsonDic:NSDictionary?
if let data_ = data {
  do{
        jsonDic = (try NSJSONSerialization.JSONObjectWithData(data_, options: NSJSONReadingOptions.AllowFragments) as? NSDictionary)
    }catch{}
    }

  }

この辺はあまり本質とは関係ありません(準備段階)。 ここからどうデータ構造をパースしていくかを考えます。

Optional chaining を使ってみる

 jsonDic?["foo"]?["bar"]

この時点でOptionalのネストにげんなりします。

Optional binding にしてみる

if let jsonDic_ = jsonDic, foo_ = jsonDic_["foo"], bar_ = foo_["bar"], bar2_ = bar_ as? NSArray{
    let value = bar2_.flatMap{ $0["baz"] }.flatMap{ $0["value"] }.flatMap{ $0 as? String}
    print(value)
}

正直、途中で放棄している感はあります。

flatMapを使う

swift2.0からの、モナド則を満たすflatMapを使ってみます。

let val = jsonDic.flatMap{$0["foo"]}.flatMap{ $0["bar"] as? NSArray }?
.flatMap{ $0["baz"]}.flatMap{$0["value"] as? String}
if let val_ = val {
    print(val_)
}

ストリームを切ることなく、直列に記述できました。 (メソッドチェーンの状態にArrayとOptionalが混在しているので、直感的に理解しにくいかもですが...)

マルチコア時代のプログラマは関数脳になろう〜Java8のススメ〜
http://tech-sketch.jp/2013/08/parallel-functional-programming-1.html

JAVA8のストリーム同様、 関数型プログラミング的なアプローチに近づいています。

もはやflatMap無しで書くのはだいぶしんどいような... こういう際に今までどうやって書いてたか思い出せません。

Promise実装のライブラリを使う際にも必須レベルですし、短縮形の シンタックスがあっても困らない気がします。出ないかなあ。