コネヒト開発者ブログ

コネヒト開発者ブログ

immutableのメリットとImmutable.jsでのModel定義

f:id:dachi023:20160902200209p:plain

こんにちは。フロントエンジニアの安達 (@ry0_adachi) です。
気付いたら前回の私の記事から2ヶ月が経ちました。時間の流れは早いですね...。

さて、今回はimmutableとそれをJSで実現するためのImmutable.jsについてです。
この記事を通して沢山の方にimmutableについて知ってもらえると嬉しいです。

immutableとは

immutableは元となっているオブジェクトに変更を加えない、加えられない状態です。
また、変更を加える、加えられることをmutableと呼びます。

続きを読む

「JQL」は、mixpanelの抱える1つの限界を打ち砕く夢である

こんにちは、PHPなどを書いている金城 (@o0h_) です。ここ暫くはファミリーマートのリーフレタスと大根のサラダセブン-イレブンの15品目のサラダボウル(和風ドレ)が私の食生活においてヘビロテしています。*1

f:id:o0h:20150214152429j:plain

話は変わるのですが、コネヒトでは、ログの収集+データの分析用途のツールの1つとしてmixpanelを利用しています。

*1:[ちょっと宣伝]コンビニサラダについて一家言ある方を、職種とわず募集しています。 ウォンテッドリーからどうぞ!

続きを読む

小規模PHPアプリケーションをDeployerでサッとデプロイする話

f:id:fortkle:20160728164803g:plain

こんにちは! 東京なのに周辺にポケストップが1つもない田舎に住んでいます、@fortkle です(世田谷公園の徒歩圏内に引っ越したいです)。

さて、みなさんはPHPアプリケーションのデプロイツールに何を使っているでしょうか。
今回コネヒトで Deployer というPHPで書かれたデプロイツールを採用したのですが、なぜDeployerなのかという理由と、実際に使っているおすすめの設定をいくつか共有したいと思います。
※ Deployerのインストール方法や基本的な使い方を知りたい場合は既にインターネット上に良い記事がたくさんあるのでそちらを参考にしてみてください。

続きを読む

新人デザイナーさんのディレクションから学んだコミュニケーションの心得

f:id:connehito:20160711094503p:plain

こんにちは、デザイナーのきよえし(@kiyoe_furuichi)です。

先日、ついにコネヒトデザインチームが結成されまして、チーフデザイナーを担当することになりました! 手探りながらチーフとしての役割を担っていて一番感じるのは、自分でつくるより何倍もつくってもらうほうが難しいということです。日々多くの発見や学びがあるなあと勉強させていただいてます。

今回は、私が新人デザイナーさんをディレクションするなかで、より良いコミュニケーションをとるために実践している"心得"をいくつかご紹介したいと思います。

制作の流れ

まず、デザインを制作する流れとして私たちは以下のように制作を行っています。

  1. タスク依頼
  2. スケジュール調整〜制作
  3. フィードバック
  4. リリース・振り返り

私が担っているものは具体的に、タスクの依頼を受けて担当デザイナーを決めることと、スケジュール管理・フィードバックを行いながら完成までフォローをすることです。 最終的なチェックはディレクターさんが行うので、なるべく手戻りが無いようクオリティの担保を行っています。
そんな役割から学んだコミュニケーションの”心得”を、それぞれのフェーズごとにまとめていきたいと思います。

1. タスク依頼 の心得

依頼するタスクはユーザーさんにとってどのようなインパクトがあるのかを伝えること

依頼するにあたって一番気をつけないといけないなと思うのは、単に"タスクをこなす"という意識を持たせないようにするということです。このタスクはなぜ必要なのか、どんなユーザーさんにどんなインパクトを与えられるのかといったところまでを想像してデザインに落とし込めるように、背景から内容までしっかり伝えるようにしています。
そのために行っていることは、依頼を文字に落として伝えるようにしています。口頭では表現でのズレが起こりやすいのでなるべく文字化して伝えるほうが良いと思っています。
最初は文字に落とすことに手間がかかるし省いたほうが良いと思っていましたが、これによって制作途中の大きな手戻りが少なくなりました。担当者がしっかりタスクの内容から想像して意図を持ったデザインが行えているためです。

2. スケジュール調整〜制作 の心得

自分の見積もり工数 + バッファをとった工数でスケジュールを立てること

スケジュールは担当デザイナーさんと一緒に立てていきます。どのくらいで制作できるかを見積もってもらい、それにバッファを足した工数でスケジュールの締め日を決めます。バッファを取るのは、万が一手戻りがあった場合の修正で締め日に間に合わなくなることを防ぐためです。
私自身、自分の工数の見積りは慣れないうちはとても難しくズレが起こることが多々ありました。はじめのうちはバッファを多めに取って、見立てが合うようになるまでの補助として持っておくのが良いと思います。

締め日を決めて、逆算して細かくスケジュールを切ること

締め日が決まったら、さらに逆算してスケジュールを立てていきます。
例えばwebサイトにバナーを掲載するタスクなら、バナーのラフ案はいつまでに上げるか、バナーの制作の期限、バナーをサイトに掲載する実装や実装したコードをレビュワーに確認を取る期限はいつまでか、などです。
細かく決めておくことによってスケジュールの巻きや遅れがより明確になり、締め日までのズレが少なくなります。スケジュールの流れを意識して着手してもらうように、切ったスケジュールはカレンダーで管理をしてリマインドを行ってあげるなどでフォローします。

3. フィードバックの心得

制作物に対してのフィードバックをする際の心得です。

まず、肯定すること

タスクの意図を想像して制作していただいた制作物をもっとより良くするためのフィードバックとなるように、まずは肯定するようにします。
フィードバックは"修正"ではなく"改善"であると考えていて、これは私の経験が大きいのですが指摘をいくつも言われる前にまず「基本良いと思います。」の言葉があるだけで、ポジティブな気持ちでフィードバックを受けることができます。マイナス点の指摘ではなく、その制作物をもっとより良くするためのフィードバックをすることがデザイナーさんとのより良いコミュニケーションにはすごく大切だと思っています。

f:id:connehito:20160710181153p:plain

なるべく文字化すること

フィードバックにおいても、タスクを依頼する際と同じようになるべく制作物のフィードバック点は文字に起こすようにします。あとから読み返せるという点と、口頭で伝える時の言葉のニュアンスによってズレがないようにするためです。(もちろん口頭でも補足は行います)
また、担当デザイナーさんに意図を説明していただくときも文字化してもらい、制作物のロジックを確認し、しっかりユーザーファーストに考えて制作できているかを見るようにしています。
文字化にすることが良いかどうかはフィードバックの内容や場合によって変わりますが、基本的にはフィードバックの回数が減ったり、同じようなやりとりを何度もすることがなくなりました。

答えは言わず、考えてもらうようにすること

フィードバック点は"こうすればいいよ"という答えではなく、答えまでの道筋を伝えるようにします。
初めのうちはもどかしいこともありますが、思考してもらうことが次の学びに繋がると思うのでなるべくそうするようにしています。例えばカラーリングのフィードバックの場合、その配色・トーンから感じる印象はそのクリエイティブの意図したい印象と合っている?どういった配色にするともっとユーザーさんに意図が伝わるか?といったようなフィードバックを行っています。

4. リリース・振り返り の心得

労いの言葉は必ずかけること

完成やリリースをしたら全力で”おつかれさま!”の言葉を伝えます。これは社内全体でそういった文化があり、リリースをした際に周知するSlackのチャンネルにて、みんなでその方の頑張りを労います。
f:id:connehito:20160711103012p:plain

見積もり工数と実工数との差分は必ず振り返ること

見積もってもらったスケジュールの反省は必ず行います。初めのうちはスケジュールの見積もりは上手く行くことが少ないです。次に生かしていくために、見積もっていた工数と実工数の比較とどこに時間がかかってしまったのかを振り返り、見積もりの精度を上げていきます。
ここでなるべく細かく振り返りができるように、都度感じたことを都度メモを取っておくようにしています。

メモ
【◯◯特集記事のバナー作成】
・制作のスケジュールから +0.5 取っての完成
・写真の選定で +0.2〜3 ほど時間を取っていた
・配色のフィードバックが多かった、修正で +0.3 ほど時間を取った

[改善するには]
・写真選定の時間を短縮したい -> 写真素材の探し方のアドバイスをする
・セレクトしてもらった写真がバナーに使いづらそう -> 写真の選び方のアドバイスをする
・フォントの選定・文字組みに苦手意識がありそう -> 参考になりそうなバナーのインプット時間を多めに取ってもらう


まとめ

いかがでしたでしょうか。以下にご紹介した心得8か条をまとめました!

1. タスク依頼
・依頼するタスクはユーザーさんにとってどのようなインパクトがあるのかを伝えること

2. スケジュール調整〜制作 の心得
・自分の見積もり工数 + バッファをとった工数でスケジュールを立てること
・締め日を決めて、逆算して細かくスケジュールを切ること

3. フィードバックの心得
・まず、肯定すること
・なるべく文字化すること
・答えは言わず、考えてもらうようにすること

4. リリース・振り返り
・労いの言葉は必ずかけること
・見積もり工数と実工数との差分は必ず振り返ること

新人デザイナーさんとのコミュニケーションは、なるべく手厚めにフォローをする・気づきから成長してもらうための工夫を入れたりしています。タスクやスケジュール感によってはできる・できない・やらないことはあると思いますが、これらの"心得"を持っておくことによってデザインチームとして、お互いに気持ち良く制作ができると思います。

私はまだディレクションを任されて3ヶ月目ですが、冒頭でも書いたように本当に"自分でつくるよりも何倍もつくってもらうほうが難しい"ということを実感しました。
この記事をまとめている際に過去を振り返っていて、こうしてまとめている心得はすべて私がこれまで実際にディレクターさんや先輩エンジニアさんからしていただいて嬉しかったコミュニケーションを参考にしていることに気がつきました。私も、嬉しいと思っていただけるようなディレクションができるようになるのが目標です。

まだまだ完璧にこなすことは難しいですが、より良いコミュニケーションがより良いプロダクトをつくると思っているので、これからも頑張ります!

Flux Utilsで始めるReact + Fluxアプリケーション開発

f:id:dachi023:20160628181620p:plain

こんにちは。エンジニアの安達(@ry0_adachi)です。

今回はReact + FluxをFlux Utilsを使って導入するための話をしたいと思います。

この記事を書こうと思った理由

普段ちょっとしたツールなんかをReactを使って実装したりするのですが、その時にReduxとかでやっているとファイル増えたりしてすごく冗長だなあと感じて、もっと薄く実装できるライブラリを使おうと思った時に手に取ったのがFlux Utilsでした。

この記事ではFlux UtilsにおけるFluxアプリケーションの実装方法に加えて、そもそもReactやFluxって今までのライブラリやフレームワークと比べて何がいいのか?について説明していきます。初歩的な書き方やjsxだったりについては話さないのでドキュメントなどを読んでいただければと思います。

ドキュメント

ReactとFluxを導入するにあたって

まず、React + Fluxの構成で今まで他のライブラリやフレームワークを使っていて感じたフロントエンドにおける課題がそもそも解決できるのかを先に考えてみます。

Reactとコンポーネント指向

ReactはFacebookが開発しているViewライブラリで、UI(画面の要素: ヘッダ, メニュー etc...)ごとにViewを分割し、複数のViewによって1つの画面を構成します。これによってUIの管理や実装が小さい単位で行えるので複雑になりにくいです。

jQueryなどで実装していると画面内のUIが大量に詰め込まれたファイルが出来上がることがありましたが、ReactではUIごとに分割するアプローチをとることでUIの実装が膨らんできても各ファイルの中身はシンプルでコンポーネントの管理が非常に楽です。

コンポーネント間での状態管理

ReactによってUIの管理は楽になりますが、アプリケーションで扱う状態の管理をどうするか?という問題があります。ReactはViewの開発をよりよくするためのアプローチなので別の手段が必要です。UIに関係しないコード(APIとの通信, データの登録, 更新, 削除)が紛れてしまえばシンプルに実装されたViewも結局は複雑で管理しにくいコードになってしまいます。

そこで、上記の問題を解決するためにFluxを利用します。

Fluxの状態管理と責務

f:id:dachi023:20160627111929p:plain

FluxはFacebookによって考案されたアーキテクチャです。Fluxの考え方に「状態の流れは常に一方向」というものがあり、これは「UI操作に伴う複雑な状態の遷移を分かりやすくするもの」だと私は思っています。

状態の流れを意識しないで実装していると、どこで状態が変化したかが追いづらいコードになります。ユーザの入力によって複数のイベントを発火させたりすることもあり、それを管理し把握できていないことで複雑な実装になりがちな上に意図していない挙動を引き起こすことも多いです。

Fluxではデータに関する責務を下記のように分割します。

  • Action (Action Creator)
  • Dispatcher
  • Store
  • View

これによって状態管理をReactが担当するViewの外に出し、Viewは外部からデータを受け取りUIを表示する、といったシンプルな実装を実現することができます。

また、状態が流れる方向は上記で記述した順番で固定されており、Fluxのルール通りに実装していれば逆流させることはありません。

Flux Utilsで実装する

Fluxと同じリポジトリに入っているFluxを実装するための最低限の機能を備えたライブラリです。Flux Utilsがカバーする範囲はStoreとViewのみですが、これまた同じリポジトリにDispatcherの実装が入っているのでこれも使います。

今回は入力した文字列をボタンをクリックしたら保存して入力された文字列全てを表示するというアプリを作ってみます。

Action (Action Creator)

Actionがやることは主に下記の3つになると思います。

  • 外部(API)との通信
  • データの加工
  • Dispatcherにtypeと値を渡す

Viewからの要求を処理してその結果をStoreに渡すのがActionの役割です。外部との通信は全てここで行い、Store内で更新のためのロジックが複雑にならないようにデータの加工とかが必要なのであればActionで加工してしまった方がStoreがスッキリするので良いと思います。データをStoreに渡す準備ができたらDispatcherに対してdispatchします。

import { dispatch } from '../dispatcher/Dispatcher'

const Actions = {
  changeText: (text) => {
    dispatch({
      type: 'change_text',
      text: text
    })
  }
}

export default Actions

dispatchする時に入力値などの他にtypeを必ず指定します。Dispatcherの先にいるStoreはtypeを見てどのコールバックを実行するかを決めるのでtypeがなければ適切に処理することができなくなります。

Dispatcher

DispatcherはActionsからdispatchされ、自身に登録されているコールバックに情報を渡す、という役割があります。また、複数のコールバックを実行する際にはその順番の制御を行うためのインタフェースも提供します。

import { Dispatcher } from 'flux'

const instance = new Dispatcher()
export default instance

export const dispatch = instance.dispatch.bind(instance);

今はDispatcherの基本実装だけで事足りるのでインスタンスをそのまま作って準備OKです。ただ、Actionから呼び出す時に毎回Dispatcher.dispatch()という書き方だと長いのでdispatchだけで呼べるように別途exportしておきます。

Store

Storeは状態(State)を管理するために下記のような役割を持っています。

  • Viewがアクセスするためのgetterの提供
  • Dispatcherに対するコールバックの登録
  • Stateの管理と生成

StoreはStateの更新を行います。更新はDispatcherに登録したコールバックからのみ実行します。そうすることで状態の流れる方向が保たれます。

今回はFlux Utilsの提供する3つのStoreの中の1つであるReduceStoreを使ったサンプルを実装します。

import { ReduceStore } from 'flux/utils'
import Dispatcher from '../dispatcher/Dispatcher'

class TextStore extends ReduceStore {
  getInitialState() {
    return []
  }

  reduce(state, action) {
    switch (action.type) {
      case 'change_text':
        state.push({ text: action.text })
        return state.slice(0)

      default:
        return state
    }
  }
}

const instance = new TextStore(Dispatcher)
export default instance

Flux Utilsではもともと保持していたStateと比較して別オブジェクトであれば再描画を行う、という仕組みのため渡ってきたstateをそのまま使うと差分がないとみなされて再描画が実行されません。なので今回はArray.sliceでやってますが、Immutable.jsなどを使ってStateの生成を行うのが良いと思います(毎回sliceとかconcatとか書きたくないですし...)。

View

Flux UtilsではContainerというReact Componentのラッパーがあります。ContainerとReact Componentをそれぞれ説明していきます。

Container

ContainerはStoreからStateを受け取り、そのStateが更新されていれば自身の配下にいるComponentに変更を通知し、再描画が実行されます。

import { Container } from 'flux/utils';
import React from 'react'
import TextStore from '../../store/TextStore'
import TextInput from '../../components/TextInput'
import TextList from '../../components/TextList'

class TextApp extends React.Component {
  static getStores() {
    return [TextStore]
  }

  static calculateState() {
    return { items: TextStore.getState() }
  }

  render() {
    return (
      <div>
        <TextInput save={this.save} />
        <TextList items={this.state.items} />
      </div>
    )
  }
}

const TextAppContainer = Container.create(TextApp)
export default TextAppContainer

TextStoreの管理するStateが変更された時にTextContainerはTextInputとTextListに変更を通知し、再描画させます。React単体だとStateにsetしたりと色々と管理することが多くViewの中が汚れてしまったりすることもありますがContainerから伝搬されてくるもの、という状態を守っていれば管理は楽になるはずです。

React Component

ComponentはContainerから受け取ったStateからUIの表示を行います。また、各イベントとactionを紐づけることでFluxのサイクルで状態が更新されます。これによって外部から与えられるStateについての管理や親にコールバックを戻すといった複雑な処理が減り、ComponentはよりシンプルにUIに集中することができます。

// TextInput

import React from 'react'
import Actions from '../../actions/Actions'

class TextInput extends React.Component {
  constructor(props) {
    super(props)
    this.state = { text: '' }
  }

  handleChange(e) {
    this.setState({ text: e.target.value })
  }

  handleClick(e) {
    e.preventDefault()
    this.setState({ text: '' })
    Actions.changeText(this.state.text)
  }

  render() {
    return (
      <div>
        <input
          type="text"
          value={this.state.text}
          onChange={this.handleChange.bind(this)}
        />
        <button onClick={this.handleClick.bind(this)}>
          Save
        </button>
      </div>
    )
  }
}

export default TextInput
// TextList

import React from 'react'

class TextList extends React.Component {
  render() {
    const items = this.props.items.map((item, index) => {
      return <p key={index}>{item.text}</p>
    })
    return (
      <div>{items}</div>
    )
  }
}

export default TextList

サンプルリポジトリ

コード全体を確認したり、実際に動かしたりするためにwebpack-dev-serverを組み込んだリポジトリを用意しました。こちらのコードも合わせて読んでみてください。

dachi023/flux-utils-example

まとめ

Reduxなどを使って実装するとどうしても最初のコード量やファイル数が増えとっつきにくい(そもそもどの位実装したらこのコストをペイできるのか、という疑問もあり...)、という方でもFlux Utilsなら実装がしやすいと思います。また、ちょっとしたアプリケーションにも向いていると思います。

ただ、Flux Utilsは最低限のFlux実装を行うものであり大規模であったり複雑な実装を求められる場合に別の手段を用いる必要もあるんじゃないかな、という不安もあります。

Connehitoとしてもこれから使っていくぞ!というところなのでまだまだ実務での知見は多くないです。これから実装を進めていく中で壁にぶつかったり何かいい手段を見つけたらこちらでまた共有しようと思います。

自分たちがユーザーでないのにどうやってママ向けプロダクトを創っているのか?に対する3つの施策とその回答

f:id:tatsushim:20160613192719j:plain

こんにちは!

最近新しいTシャツを着ると必ずメンバーに突っ込まれます。CTOの島田(@tatsushim)です。
今日はよく面接で質問をいただく「自分たちがユーザーでないのにどうやってママ向けプロダクトを創っているのか?」という疑問にお答えしたいと思います。

データだけでは生まれない「改善案」

私を含む開発チームのメンバーは全員「ママ」ではありません。 そこでデータドリブンで開発することにより、定量的に機能の良し悪しを判断することで改善のサイクルを回しています。 ( 独身男性のためのデータドリブン講座 // Speaker Deck )
しかし、データから異常値を発見しても、それが実際何を意味するのかが理解できないとプロダクトの改善はできません。例えば
「恐らくユーザーさんはお子さんを一旦寝かしつけて、落ち着いたところで僕らのサービスを使ってくれたんだな。だから22時頃がピークタイムになるんだ」
と想像をした上で、改善案を練る必要があります。
そこで、具体的な改善案を出したり、エンジニア自身が想像力を身につけるために会社で行っている3つの施策についてご紹介したいと思います。

プロダクトの改善案を生み出す3つの施策

続きを読む

Google I/O 2016で発表されたFirebase Analyticsを使ってみた

f:id:Utmrer:20160519231110j:plain

こんにちは、最近AbemaTVでフリースタイルダンジョンを深夜に見る習慣が出来て寝不足気味の田村(@Utmrer)です。

みなさんはアプリのトラッキングにどのAnalytics Toolを使っていますか?Google AnalyticsやFlurry, Facebook Analyticsなどいろいろありますが、弊社では主にMixpanelを使ってトラッキングしています。
Mixpanelは一定のデータ量までは無料で使う事ができるので最初は良かったのですが、サービスが成長してくると結構な金額になってきてます。
そこで日々良いツールは無いかと探していたのですが、Google I/Oで発表されたFirebase Analyticsが無料という話を聞いたので試しに使ってみることにしました。

簡単なトラッキング

プロジェクトの作成やアプリの追加は公式のドキュメントを参考にしてください。Console画面はほとんど日本語化されているのでとてもわかりやすいです。

FirebaseはCocoaPodsに対応しているのでpod 'Firebase'を追加してpod installするだけでインストール出来ます。
それではユーザーの会員登録イベントをトラッキングしてみます。

// AppDelegate
override func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
    FIRApp.configure()
}
// RegisterViewController
func register(sender: AnyObject) {
    FIRAnalytics.logEventWithName(kFIREventSignUp, parameters: nil)
}

AppDelegateでAnalyticsの設定を行って、ViewControllerで登録イベントを発火させただけのシンプルなイベントトラッキングです。
FIRAnalytics.logEventWithNameの最初の引数がイベント名で2番目の引数がそのイベントのパラメータになります。
kFIREventSignUpというのはFirebase SDKが定数として持っているイベント名で「こういうのトラックするといいんじゃない?」という声が聞こえてきそうです。 他にもkFIREventTutorialBeginkFIREventUnlockAchievementなど重要そうなイベント名の定数が設けられています。

もちろんイベント名は定数だけでなく、オリジナルなイベント名も入れられます。

func watch(sender: AnyObject) {
    FIRAnalytics.logEventWithName("WatchAnimation", parameters: ["Series": "LoveLive"])
}

これで「ラブライブを観た」というイベントがトラック出来ました。

ユーザー情報の設定

Google AnalyticsのようにユーザーIDやパラメータが設定できます。ユーザーIDを設定した時のAnalyticsでの挙動みたいな文言は見つからなかったのですが、ユニバーサル アナリティクスのようにUUの精度が上がったりするのでしょうか。

// AppDelegate
override func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
    FIRApp.configure()
    FIRAnalytics.setUserPropertyString("のぞみ", forName: "name")
    FIRAnalytics.setUserID("9")
}

AppDelegateのハンドリング

FirebaseにはAppDelegateに関するトラッキングが3つ用意されています

  • + handleEventsForBackgroundURLSession:completionHandler:
  • + handleOpenURL:
  • + handleUserActivity:

これらは自動で行われるので実装する必要はなく、手動でやりたい場合は設定ファイルに書いて自分で実装するようです。
これらのメソッドが何をやるのか詳しくは書いてないですが、Universal LinkやAppIndexing, Spotlightなどのアプリ起動のReferrerに関するトラッキングをしてくれそうな気がします。

使ってみて

このライブラリで開発者がやることはFIRAnalytics.logEventWithNameぐらいです。かなりクラス数やAPI数が少なく、すぐ使えると思います。
逆に言うと、Firebase Analyticsだけではあまり機能が無い、とも言えます。Console画面もMixpanelに比べると

  • ユーザー継続率のコホートしかなく、カスタムイベントのRetentionが見れない
  • Formulasがない(Register/Launchで新規率をグラフにしたりする機能)
  • iOSとAndroidの数値を合算して表示したりできない(Google Analyticsの様に複数のOSを1つのプロパティで管理できない)

などがデメリットでMixpanelからスイッチすることは難しいな、という感じでした。
一方で、Firebase AnalyticsのログはBigQueryに吐けるらしいのでお手製で解析ツールを作れるのなら自由度は上がると思います。
また、NotificationsやRealtime Database, Crash Reportingなど他のFirebaseサービスと連携するならロックインされた方がメリットが大きかもしれませんね。
Google AnalyticsのSDKより断然使いやすいので1度試してみてはどうでしょう!