2016年5月22日日曜日

iOSでFirebase Analyticsを使う


(引用元:https://firebase.google.com)

セットアップ

公式Page
https://firebase.google.com/docs/ios/setup

CocoaPodを利用してインストールします。
podの初期設定を実行。
pod init
Podfile を開いて以下を追加します。
pod 'Firebase/Core'
podのインストールを実行します。
pod install

Analytics以外の機能を使用するには、次の公式サイトに記載されているpodを追加しましょう。
https://firebase.google.com/docs/ios/setup#available_pods


import UIKit
import Firebase

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // init Firebase
        FIRApp.configure()
        return true
    }
}


イベント送信方法

以下、宣言済みのイベントがあり、ヘッダーファイルに定義されているので参照。
イベント:FIReventNames.h
パラメータ:FIRParameterNames.h


FIRAnalytics.logEventWithName(kFIREventSelectContent, parameters: [
  kFIRParameterContentType:"cont",
  kFIRParameterItemID:"1"
  ])


ユーザープロパティ

アプリのユーザーをカテゴライズすることが可能です。
FIRAnalytics.setUserPropertyString(food, forName: "favorite_food")

AndroidでFirebase Analyticsを使う


(引用元:https://firebase.google.com)

セットアップ

セットアップ方法は、必ず最新の情報を参照ください。
公式Page
https://firebase.google.com/docs/android/setup

プロジェクトのbuild.gradleに次のclasspathを追加する。
buildscript {
  dependencies {
    classpath 'com.google.gms:google-services:3.0.0'
  }
}


appのbuild.gradleに次のpluginを追加する。

// Add to the bottom of the file
apply plugin: 'com.google.gms.google-services'

Firebase Analyticsを使用するには、次のdependenciesを追加する。
dependencies {
    compile 'com.google.firebase:firebase-analytics:9.0.0'
}

Analytics以外の機能を使用するには、次の公式サイトに記載されているdependenciesを追加しましょう。
https://firebase.google.com/docs/android/setup#available_libraries

イベントの送信

イベントの種類
以下、宣言済みのイベントがあります。
https://support.google.com/firebase/answer/6317485?hl=en&ref_topic=6317484
FirebaseAnalytics.Event
https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event

  • Automatically collected events
  • Events: All apps
  • Events: Retail/Ecommerce
  • Events: Jobs, Education, Local Deals, Real Estate
  • Events: Travel (Hotel/Air)
  • Events: Games
  • Automatically collected user properties



宣言済みのイベント以外にも、オリジナルのイベントの追加が可能ですが以下の制限があります。

  • イベントは500種類まで
  • イベント名はユニークな名前であること
  • イベント名のPrefixに"firebase_"を付けない(SHOULD)
  • イベント名は32文字の、アルファベットとunderscores( _ )のみ


パラメータ
各イベントに25個までのパラメータを付加することが可能です。定義済みのパラメータがあります。
FirebaseAnalytics.Param
https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Param

イベント同様、オリジナルのパラメータの追加が可能です。
  • パラメータ名は24文字の、アルファベットとunderscores( _ )のみ
  • パラメータの値は36文字まで
  • Prefixに"firebase_"を付けない(SHOULD)


イベントの送信方法
Bundleにパラメータを詰めて、logEventにイベント名とBundleを渡すだけ送信可能です。

FirebaseAnalytics mFirebaseAnalytics = FirebaseAnalytics.getInstance(this);
Bundle bundle = new Bundle();
bundle.putString(FirebaseAnalytics.Param.ITEM_ID, id);
bundle.putString(FirebaseAnalytics.Param.ITEM_NAME, name);
bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, "image");
mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.SELECT_CONTENT, bundle);


ユーザープロパティ

アプリのユーザーをカテゴライズすることが可能です。
FirebaseAnalytics.UserProperty
https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.UserProperty

アプリごとに25種類までユーザー属性を追加することができます。

  • ユーザープロパティ名は24文字まで、アルファベットとunderscores( _ )のみ
  • ユーザープロパティの値は36文字まで
  • Prefixに"firebase_"を付けない(SHOULD)


FirebaseAnalytics mFirebaseAnalytics = FirebaseAnalytics.getInstance(this);
mFirebaseAnalytics.setUserProperty("favorite_food", mFavoriteFood);

2016年4月28日木曜日

[RxSwift] SectionありのDataSourceを生成する

本家のサンプルにも記載されている通り、RxDataSourcesを使用する方法があります。
https://github.com/ReactiveX/RxSwift/tree/master/RxExample/RxDataSources

RxDataSources:
https://github.com/RxSwiftCommunity/RxDataSources

Podfileに記載するなりしてインストールしてください。

SectionありのdataSourceを生成する

RxTableViewSectionedReloadDataSourceを使用します。SectionModelでSectionに表示するelementを指定します。

let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, String>>()
let items = Observable.just([
    SectionModel(model: "First section", items: [
            "aaaa",
            "bbbb",
        ]),
    SectionModel(model: "Second section", items: [
            "cccc",
            "dddd",
        ])
    ])


セルの生成

以下のように、indexPathとelementが引数に入っているのでコールバック内でセルを生成する。
dataSource.configureCell = { (_, tableView, indexPath, element) in
    let cell = tv.dequeueReusableCellWithIdentifier("Cell")!
    cell.textLabel?.text = "\(element) @ row \(indexPath.row)"
    return cell
}


データとTablewViewをBindする

items
    .bindTo(tableView.rx_itemsWithDataSource(dataSource))
    .addDisposableTo(disposeBag)


ヘッダーのカスタマイズ

UITableViewDelegateで処理する必要があります。
以下のように、ヘッダー用のViewとHightを返すモジュールを実装します。
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let view = UITableViewHeaderFooterView()
    view.textLabel?.text = dataSource.sectionAtIndex(section).model ?? ""
    return view
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 44
}

2016年4月16日土曜日

[RxSwift] UITextFieldの入力テキストと通知

ViewModel側にObserverを作成する。ObserverでText更新を受け取ってViewModelの処理を行う。


let disposeBag = DisposeBag()
var account:Variable = Variable("")

// 参照方法
account.value



UITextFieldのrx_textとViewModelのObserverをBindする。
Textが更新されると、ViewModel側のObserverまで届く。
let disposeBag = DisposeBag()
accountTextField.rx_text
  .bindTo(viewModel.accountSubject.asObserver())
  .addDisposableTo(disposeBag)


プログラム上でTextを更新する場合(ストリームに流す場合)、sendActionsForControlEventsでValueChangedイベントをUITextFieldに流し込む。
accountTextField.text = "test"
accountTextField.sendActionsForControlEvents(.ValueChanged)

2016年4月15日金曜日

[RxSwift] UITableViewでカスタムセルを使用する

UITableViewで表示するデータをVariableで宣言するし、データの更新はvalueにセットする。

    let items = Variable<[Item]>([])
    // 新しいデータで更新
    self.items.value = updateItems

UITableViewにデータをBindingするには、rx_itemsWithCellIdentifierをコールする。
カスタムセルを使用する場合は、第二引数のcellTypeにセットが必要です。

    viewModel?.items
        .asDriver()
        .drive(tableView.rx_itemsWithCellIdentifier("CustomTableViewCell",cellType: CustomTableViewCell.self)) { row, item, cell in
            cell.item = item
            cell.tag = row
        }
        .addDisposableTo(disposeBag)


セルの選択はrx_itemSelectedを使用します。
tableView.rx_itemSelected
    .subscribeNext { [unowned self](indexPath) in
        self.tableView.deselectRowAtIndexPath(indexPath, animated: true)
        // get cell
        let cell: CustomTableViewCell = self.tableView.cellForRowAtIndexPath(indexPath)! as! CustomTableViewCell        
    }
    .addDisposableTo(disposeBag)

EditingStyleのセット

rx_itemsWithCellIdentifierを使用すると、DefaultでEditting可能になってしまいます。
不要の場合はUITableViewCellEditingStyle.Noneをセットしましょう。

func tableView(tableView: UITableView, editingStyleForRowAtIndexPath: NSIndexPath) -> UITableViewCellEditingStyle {
    return UITableViewCellEditingStyle.None
}

2016年4月12日火曜日

[Swift]循環参照(相互参照)によるメモリリーク

MVVM移行時にクラス分けを行った結果、ViewControllerのメモリリークが発生しました。deinitが呼ばれず期待動作となりませんでした。


循環参照(相互参照)によるメモリリーク

以下のような状態になると、メモリリークが発生します。
何も考えずにViewModelのDelegateを追加、DelegateにViewControllerをセットでメモリリーク発生


解決策

弱参照になるようにDelegate用プロトコルをclass継承し、weakで保持できるように対応。


protocolにclass継承追加
 public protocol ViewModelDelegate : class{
 }


weak追加
 weak var delegate: ViewModelDelegate?

2016年4月3日日曜日

[Swift]UIButtonでPressed状態の装飾を行う

AndroidならSelectorのxmlファイルを作成すればボタンの状態に合わせた装飾できます。
Swiftではボタンの文字などは次のfuncで設定可能です。

func setTitle(_ title: String?,forState state: UIControlState)
func setTitleColor(_ color: UIColor?,forState state: UIControlState)
func setTitleShadowColor(_ color: UIColor?,forState state: UIControlState)
func setBackgroundImage(_ image: UIImage?,forState state: UIControlState)

layerなどで装飾をする場合は、次のようにhighlightedのdidSetで変更可能です。

@IBDesignable class MyUIButton: UIButton {
    
    @IBInspectable var borderColor :  UIColor = UIColor.blackColor()
    @IBInspectable var borderHighLightedColor :  UIColor = UIColor.clearColor()
    
    override internal func awakeFromNib() {
        super.awakeFromNib()
    }
    
    override var highlighted: Bool{
        didSet{
            if (highlighted) {
                self.layer.borderColor = borderHighLightedColor.CGColor
            } else {
                self.layer.borderColor = borderColor.CGColor
            }
        }
    }
    // Attributes Inspectorで設定した値を反映
    override func drawRect(rect: CGRect) {
        self.layer.borderColor = borderColor.CGColor
    }

}