2018年10月17日水曜日

Xcode10 + mojave で CreateML を触ってみる

概要

CreateML が正式に試せるようになったので Playground でとりあえず試してみました
学習されるデータは画像情報になります

環境

  • macOS 10.14
  • Xcode 10.0

Playground なプロジェクトを作成する

CreateML を最も簡単に試せるのは Playground です
Xcode を立ち上げて「Get started with a playground」を選択します
createml1.png

macOS を選択する

Playground は macOS アプリとして動作させるので macOS を選択しましょう
以下では Single View を選択していますが何でも OK です
createml2.png

コーディング

以下のコードを貼り付けましょう

import CreateMLUI

let builder = MLImageClassifierBuilder()
builder.showInLiveView()

左側の矢印から Playground を実行します
そして右上の「Show the Assistant editor」から Live view を表示します
createml3.png

Live view が表示されない場合は Xcode を立ち上げて Assistant editor を開いて少し待ってみましょう
ダイアログが表示され許可すると Live view が立ち上がると思います
それでもダメな場合は再起動して許可のダイアログが表示されるまで繰り返してください

学習および評価データ

Playground の CreateML は画像情報を学習し評価してくれます
今回は犬と猫の画像データをここから取得しました
犬と猫の画像データがそれぞれ 1 万件ほどあります
学習データのラベル付けやテストデータのラベル付けはディレクトリに分けるだけで OK です

今回は犬の学習データ (train/dog) とテストデータ (test/dog)
そして猫の学習データ (train/cat) とテストデータを (test/cat) をそれぞれ 100 ファイルづつコピーして使いました

1 万ファイルすべてを使いたい場合は 9000:1000 くらいで学習:テストで分けると良いと思います

$ tree -d
.
├── test
│   ├── cat (1.jpg から 100.jpgをコピー)
│   └── dog (1.jpg から 100.jpgをコピー)
└── train
    ├── cat (101.jpg から 200.jpgをコピー)
    └── dog (101.jpg から 200.jpgをコピー)

学習データと評価データの選択

Live view にプルダウンがあるのでクリックすると「Training data」と「Validation data」を選択する部分があります
ここに先程作成したデータのディレクトリをそれぞれ選択します
createml4.png

あとは「Train」を押せば学習が始まります
学習している画像が次々と表示されると思います

モデルを保存する

学習が完了するとモデルを保存することができます
与えたテストデータでは 97% の精度が出ていることが確認できます
コンソールには更に詳しい学習状況が表示されています

createml5.png

ImageClassifier.mlmodel という名前のモデルが保存されていると思います
作成したモデルは CoreML を使うことで iOS アプリに組み込むことができます
https://developer.apple.com/documentation/vision/classifying_images_with_vision_and_core_ml

最後に

とりあえず CreateML で画像認識してみました
モデルだけであればかなり簡単に作成できました
あとはモデルを使う方法を学べばすぐにアプリにできると思います

他にもテキストデータを学習したり分類などもできるようです

参考サイト

2018年10月16日火曜日

ブックマークの一覧を取得する方法

概要

Firefox の extension に bookmark の API があります
これを使えばブックマークのタイトルや URL を取得することができます
今回はブックマークツールバーにあるブックマークの一覧を取得してみました

環境

  • macOS 10.13.6
  • Firefox 62.0

manifest.json

  • vim manifest.json
{
  "manifest_version": 2,
  "name": "Test bookmarks",
  "version": "1.0",
  "description": "Fetch bookmarks info",
  "background": {
    "scripts": ["main.js"]
  },
  "permissions": [
    "bookmarks"
  ]
}

permissions.bookmarks が必要です

main.js

  • vim main.js
browser.bookmarks.getTree(function(results) {
  results[0].children.forEach(function(toolbar) {
    if (toolbar.id == "toolbar_____") {
      toolbar.children.forEach(function(bookmark) {
        console.log(bookmark.id);
        console.log(bookmark.url);
      })
    }
  });
});

browser.bookmarks.getTree でブックマーク全体の情報を取得します
次に results[0].children で直下にあるブックマークのディレクトリ情報を取得します
このディレクトリの中にある "toolbar_____" という ID を持つディレクトリがブックマークツールバーになります

この配下の toolbar.children でブックマークの一覧の配列を取得しあとは forEach で回すだけです

取得できる情報は BookmarkTreeNode のオブジェクトになります
今回は id と url の情報を表示しています

動作確認

about:debugging からデバッグ画面を表示して確認すると以下のように表示されると思います
bookmark1.png

最後に

Firefox の extension でブックマークの操作をしてみました
今回は取得しかしませんでしたがブックマークの登録や削除などもできます
提供されている API は以下の参考サイトのリンクに記載されています

参考サイト

2018年10月15日月曜日

Firefox の WebExtension で簡単なローカルストレージを使う方法

概要

Firefox の Webextension で設定内容などを保存したい場合にはブラウザのローカルストレージが使えます
特にクラウドでデータを連携する必要がない場合などは簡単かつ軽量に使えるので便利です
今回は簡単な使い方を紹介します

環境

  • macOS 10.13.6
  • Firefox 62.0

manifest.json

  • vim manifest.json
{
  "manifest_version": 2,
  "name": "Local storage demo",
  "version": "1.0",
  "description": "Store and save your data in local storage",
  "icons": {
    "48": "icons/border-48.png"
  },
  "background": {
    "scripts": ["main.js"]
  },
  "permissions": [
    "storage"
  ]
}

permissions.storage が必要になります

main.js

  • vim main.js
browser.storage.local.get("config", function(value) {
  if (value.config === undefined) {
    browser.storage.local.set({
      config: {
        count: 1
      }
    });
  } else {
    var c = value.config.count;
    c++;
    console.log(c);
    browser.storage.local.set({
      config: {
        count: c
      }
    });
  }
});

あまり良いサンプルじゃないかもしれません、、
ローカルストレージからのデータの取得は browser.storage.local.get で行います
ローカルストレージへのデータの保存は browser.storage.local.set で行います

データは JSON 形式で保存します
そして取得するときも JSON になるのでキーをドットでつないで参照します
初回はデータがなく undefined になるので、その場合は初期化する処理を書いています

今回のサンプルの場合 extension を読み込むたびにカウントアップしていくサンプルになります
about:debugging の画面でデバッグ画面を開いて何度も最読み込みするとカウントアップするのがわかると思います
普通はオプション画面などで使うと思います
ちなみにオプション画面は manifest.json に options_ui を定義することがで実現できます

"options_ui": {
  "page": "options.html"
}

この HTML 内で更に js ファイルを参照してそこで browser.storage.local.set を呼ぶ感じです
こうすることでページやタブを跨いでもデータを横断して参照することができるようになります

最後に

Firefox の Webextension でローカルストレージを使ってみました
データは extension が削除されたりユーザがブラウザのキャッシュ情報などを意図的に削除するとなくなります

用途してはテンポラリー的な感じかなと思うので重要なデータなどはクラウドストレージなどと連携すると良いと思います
storage.sync などを使えば Firefox Account 同士でデータを共有することができます
ただその場合は認証なども必要になるので少し大変な実装になるかとは思います

2018年10月14日日曜日

Patreon の OAuth 認証を使ってみた

概要

Patreon には OAuth の仕組みがありこれを使えば Patreon のサーバ情報に API を使ってアクセスすることができます
今回は OAuth を実現するためのログイン画面の使い方から OAuth 後の API の呼び出しまで基本的な流れを試してみました

環境

  • macOS 10.14
  • Ruby 2.5.1p57
  • patreon-ruby 0.5.0

クライアントアプリ作成

まずは OAuth 用のクライアントアプリを作成しましょう
このページから作成できます
コールバック用の URL は localhost で動作させるアプリを指定します

patreon_oauth3.png

クライアントを作成すると「Client ID」と「Client Secret」が取得できるのでメモしておきましょう

ライブラリインストール

  • bundle init
  • vim Gemfile
gem "sinatra"
gem "patreon"
  • bundle install --path vendor

アプリ作成

今回のアプリの流れとしては

  • Patreon でログインページへ遷移
  • ログインできた場合はコールバック用のページでトークンを取得
  • 取得したトークンを使って Patreon の情報を取得

になります

ログイン画面の作成

まずは Patreon のログイン画面に遷移させるページを作成します

  • vim app.rb
require 'sinatra/base'

class TestOAuth < Sinatra::Base
  get '/login' do
    erb :login
  end
end

/login にアクセスした場合にログイン画面に遷移するリンクを表示します

  • mkdir views
  • vim views/login.erb
<html>
<head>
</head>
<body>
  <a href="https://www.patreon.com/oauth2/authorize?response_type=code&client_id=0xmmvlIcKC0PAhdZHdQ31myNO1qPD4MDQBqHOLoZQ19n5DCmfupfyZdlhwv8ikMe&redirect_uri=http://localhost:9292/callback">login</a>
</body>
</html>

Patreon のログイン画面へのリンクにはフォーマットが決められており https://www.patreon.com/oauth2/authorize に対して response_typeclient_idredirect_url をパラメータに付与してリクエストします
ここで client_id は先程クライアント作成時にメモしておいた「Client ID」を記載してください
また redirect_url もクライアントアプリを作成するときに指定した URL を指定してください
間違っている場合ログイン画面が表示されません

コールバック用のページの作成

ログインに成功した場合に呼び出されるコールバック用のページを作成します
app.rb にコールバック用のリクエストを受け付けるルーティング /callback を追加します

  • vim app.rb
require 'sinatra/base'
require 'patreon'

class TestOAuth < Sinatra::Base
  get '/login' do
    erb :login
  end

  get '/callback' do
    client_id = '0xmmvlIcKC0PAhdZHdQ31myNO1qPD4MDQBqHOLoZQ19n5DCmfupfyZdlhwv8ikMe'
    client_secret = 'Xex5ENhpLeZwv7UdZWBF2HS6bLqwE6cUYQJVkQWevcBMBc2bINhKKeh-l069Uypq'
    redirect_url = 'http://localhost:9292/callback'

    oauth_client = Patreon::OAuth.new(client_id, client_secret)
    tokens = oauth_client.get_tokens(params['code'], redirect_url)

    api_client = Patreon::API.new(tokens['access_token'])
    user = api_client.fetch_user()
    @user_data = user.data
    erb :callback
  end
end

client_id, client_secret は作成したクライアントのものを指定してください
その 2 つからトークンを取得するための oauth_client を作成します
コールバックされたページには code というパラメータが付与されて呼び出されます
その code と redirect_url そして oauth_client を使って get_tokens メソッドを呼び出すことでトークン情報を取得することができます

トークンにはいくつか種類がありますが API をコールするために必要なのは access_token になります
ハッシュとして受け取れるので access_token にアクセスしましょう

あとはトークンを元に Patreon::API で API をコールするためのクライアントを作成し fetch_user などのメソッドをコールすれば OK です

今回は取得したデータをテンプレートに渡してそちらでアイコンと名前を表示します

  • vim views/callback.erb
<html>
<head>
</head>
<body>
  <h1><%= @user_data.full_name %></h1>
  <img src="<%= @user_data.thumb_url %>">
</body>
</html>

動作確認

  • bundle exec rackup config.ru

でアプリを起動しましょう
あとは localhost:9292/login にアクセスするとログインへのリンクが表示されるのでそれを踏み Patreon のログイン画面でログインすればユーザ情報が表示されるはずです

patreon_oauth_demo.gif

最後に

Patreon の OAuth 機能を使ってログインから情報を取得するまでの基本的な流れを紹介しました
OAuth 自体に特に難しかった点はなかったのですが、Ruby のクライアントライブラリの使い方などは知っておく必要がありそうです
公式に Sinatra の OAuth のサンプルもあったのでそれも参考にすると良いかもしれません

今回使用したクライアントアプリはすでに削除しているので client_idclient_secret は使えませんのでご注意ください

参考サイト

2018年10月13日土曜日

ReactJS 入門

概要

ブラウザだけで ReactJS に入門してみました
簡単な英語のチュートリアルがあったのでそれの動かし方を説明します
とりあえず動かしてみたい人向けのチュートリアルになります

環境

  • macOS 10.14
  • ReactJS 16

サンプルコード

雛形の index.html の作成

  • vim index.html
<html>
<head>
  <script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
  /* 
  ここにコードを記載します
  */
  </script>
</body>
</html>

head の部分に ReactJS を使うためのスクリプトファイルを追加します
babel-standalone も使うのでそれも追加します
どちらも CDN で配信してくれているのでそれを利用します
body 内に ReactJS のコードを記載することで DOM をレンダリングしていきます

Hello world を出力する DOM を追加する

先程のコードの「ここにコードを記載します」に追加します

  • vim index.html
<html>
<head>
  <script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
  class Hello extends React.Component {
    render() {
      return <h1>Hello world!</h1>;
    }
  }
  ReactDOM.render(
    <Hello />, 
    document.getElementById("root")
  );
  </script>
</body>
</html>

React.Component を継承したクラスで render メソッドを実装します
そして ReactDOM.render で作成したクラスと描画する DOM の情報を指定します

動作確認

あとは index.html をブラウザで開けば「Hello world!」と表示されます
こんな感じで Component を作成して render する感じになります

props と state

ReactJS では props と state というデータが使えます

props

props は ReactDOM.render でクラスを指定した際に属性名と値を指定すると、その値をクラス側で参照することができます
具体的には以下のように変更します

class Hello extends React.Component {
  render() {
    return <h1>Hello {this.props.name}!</h1>;
  }
}
ReactDOM.render(
  <Hello name="hoge" />, 
  document.getElementById("root")
);

JSON を渡すこともできます

const d = {"first":"hoge","family":"fuga"}
class Hello extends React.Component {
  render() {
    return <h1>Hello {this.props.name.family}!</h1>;
  }
}
ReactDOM.render(
  <Hello name={d} />, 
  document.getElementById("root")
);

state

state は値が変更されると再度 render を走らせることができるデータです
ボタンをクリックすると state を変更し表示内容を変更してみます
以下のように ReactJS の部分を書き換えます

class Hello extends React.Component {
  render() {
    return(
      <div>
        <h1>Hello {this.state.name}!</h1>
        <button onClick={this.updateName}>Click me!</button>
      </div>
    )
  }
  constructor() {
    super();
    this.state = {
      name: ""
    };
    this.updateName = this.updateName.bind(this);
  }
  updateName() {
    this.setState({
      name: "hawk"
    });
  }
}
ReactDOM.render(
  <Hello />, 
  document.getElementById("root")
);

ReactDOM.render は元のやつに書き換えます
まず render() メソッドで初期状態の DOM を return するようにします
ボタンを新たに設置しクリック時に onClick={this.updateName} をコールさせます
Hello 側は state に応じた値を表示させるため {this.state.name} を設定します

updateName メソッド内で setState をコールします
state を変更する場合は直接プロパティを参照することはせず setState メソッドを呼ぶようにしましょう
これでクリック時の挙動は定義できました

あと新たに追加したのは constructor() になります
これは Hello クラスが呼び出された場合に一番始めにコールされる初期化用のメソッドです
ここで this.state に対して初期化します
今回の場合、空文字で初期化しているので画面を呼び出した際には名前は何も表示されません

そして最後に this.updateName = this.updateName.bind(this); しています
これは updateName メソッドに this を bind しているのですがこれをすることでどうなるかというと updateName 内で this が参照できるようになります

これで動作確認するとボタンが表示されボタンをクリックすると名前が表示されるのがわかると思います

最後に

ReactJS を使って簡単な DOM のレンダリングと基本となるデータの使い方を学びました
かなり簡単なチュートリアルだったので更にやってみたい方は公式のチュートリアルがオススメです
ゲームを開発するチュートリアルっぽいです
他には今回参考にさせて頂いたチュートリアルの続きでチャットアプリを開発するチュートリアルもあるのでこれも良いかもしれません

フロントエンドのフレームワークはやたらありそれぞれが独自の思想を持っているケースが多いのですべてを覚えてることは不可能ですが、ReactJS や Angular, jQuery あたりのメジャーどころは覚えておいて損はないかと思います

参考サイト

2018年10月12日金曜日

macOS で react-native 入門

概要

macOS で react-native を使って iOS アプリを開発してみました
なお今回は expo を使わずに react-native-cli を直接使います

環境

  • macOS 10.14
  • Xcode 10.0 (10A255)
  • node 10.1.0
  • watchman 4.9.0
  • react-native-cli 2.0.1
  • react-native 0.57.2 -> 0.57.1

事前準備

まずは環境を構築します

  • brew install node
  • brew install watchman

node は 8.3 以上が必要です

react-native-cli のインストール

react-native-cli プロジェクトを作成したり

  • npm install -g react-native-cli

Xcode コマンドラインツールのインストール

Xcode を開き Preferences -> Locations からインストールできます

新規アプリケーションの作成

とりあえずサンプル用のアプリケーションを作成します

  • react-native init AwesomeProject

いろいろとダウンロードが始まります
プロジェクトの作成が完了し各プラットフォームでのビルド方法が表示されれば OK です

To run your app on iOS:
   cd /Users/hawksnowlog/work/AwesomeProject
   react-native run-ios
   - or -
   Open ios/AwesomeProject.xcodeproj in Xcode
   Hit the Run button
To run your app on Android:
   cd /Users/hawksnowlog/work/AwesomeProject
   Have an Android emulator running (quickest way to get started), or a device connected
   react-native run-android
(node:67987) ExperimentalWarning: The fs.promises API is experimental

ios/AwesomeProject.xcodeproj を Xcode で開き設定変更

このままビルドしても Print: Entry, ":CFBundleIdentifier", Does Not Exist のエラーでビルドできません
一度プロジェクトの設定を変更します

  • File -> Project Settings
  • Advanced

で Build Location を以下のように変更します
react-native2.png

Products と Intermediates の先頭に build/ を入力しました
またプルダウンは「Relative to Workspace」を選択します

とりあえず動かしてみる

  • cd AwesomeProject
  • react-native run-ios

でシミュレータが起動しテストできます
8081 で LISTEN するビルドサーバが立ち上がるのですでにポートを使っている場合はプロセスを停止しましょう
ビルドしてパッケージを転送するのでアプリが起動する前に結構時間がかかります
またやたら warning が出ている感じがしますが気にせず進めます
環境によるかもしれませんがシミュレータは iPhone6 が立ち上がりました
react-native3.png

ターミナルでは以下のようになっていれば成功です
これが 100% にならない場合はどこかでビルドが失敗しています
react-native4.png

error: bundling failed: Error: Unable to resolve module ./../react-transform-hmr/lib/index.js

どうやら react-native 0.57.2 で出るようです (参考)
0.57.1 にダウングレードしてビルドし直しましょう

  • vim package.json
"react-native": "0.57.1"
  • npm add @babel/runtime
  • npm install
  • rm -rf $TMPDIR/react-*; rm -rf $TMPDIR/haste-*; rm -rf $TMPDIR/metro-*; watchman watch-del-all

をしてから再度 react-native run-ios してみてください

ちょろっとコードを改修する

App.js を編集しましょう
とりあえずウェルカムメッセージの部分を変更しました

<Text style={styles.welcome}>Welcome to React Native!!!</Text>

保存して Command + r をシミュレータ上で実行するとアプリの変更が反映されていると思います

最後に

react-native を使って iOS アプリをビルドしてみました
Android 用のビルド環境を作れば Android でもビルドできると思います

今回は最新バージョン (0.57.2) でバグのような挙動があったので苦労しましたが本来であればエラーは出ずにビルドできると思います
一度ビルドしてアプリを起動すれば変更などはすぐに確認できるのは良いかなと思います

ただ最近は Airbnb が react-native での開発をやめたりとあまり良いニュースがないイメージです
もともと Swift なり Kotlin で native 開発できるのであれば多少手間でも将来性を考えると native を使ったほうが良いかなと思います

参考サイト

2018年10月11日木曜日

Firefox の Webextension で Notifications を出す方法

概要

Firefox の Webextension では新たに Notification が使えるようになりました
今回は簡単な使い方を紹介します

環境

  • macOS 10.13.6
  • Firefox 62.0

manifest.json

  • vim manifest.json
{
  "manifest_version": 2,
  "name": "Notifications sample",
  "version": "1.0",
  "description": "Display a notification when starting",
  "icons": {
    "48": "icons/border-48.png"
  },
  "background": {
    "scripts": ["main.js"]
  },
  "permissions": [
    "notifications"
  ]
}

ポイントは permissionsnotifications を追加するところです

main.js

  • vim main.js
browser.notifications.create({
  "type": "basic",
  "iconUrl": browser.extension.getURL("icons/icon-48.png"),
  "title": "Hello",
  "message": "This notification is test"
});

type は basic を使っています
他にも image や list, progress などがあります
詳細はこちらをご覧ください
iconUrl は通知時にアイコンを指定することができます
指定のアイコンがない場合はデフォルトで Firefox のアイコンが表示されます
title, message は通知に表示する文字列情報になります

今回の場合はアドオンを読み込みした時点で通知します
本来であればクリックのイベントやコールバック処理などで呼び出しましょう

動作確認

about:debugging から manifest.json を読み込みましょう
すると以下のように通知が来ると思います
notification1.png

最後に

Firefox の Webextension で Notifications 機能を使ってみました
background で動作させる場合には window.alert などが使えないので代用として Notifications は良いかもしれません
また HTML が不要なのも嬉しい点かなと思います

通知時のイベントもあり onButtonClicked, onClicked, onClosed, onShown があります (参考)

通知はウザがられがちですが使い方によっては便利なのでうまく活用してみてください