yo_waka's blog

418 I'm a teapot

ReactiveCocoa Tokyo

先週になりますが、「ReactiveCocoa Tokyoというイベントがあり、そこでfreee社での導入の経緯やMVVMでのReactiveCocoaの使い方について話してきました。

ReactiveCocoaは役割上ロックインされがちで、そこをなるべくゆるやかに導入していくにはというのが ninjinkunさんの発表に対して、ロックインされると各レイヤに統一感出ていいよと、対比ぽい感じができたのでよかったです。

ReactiveCocoaの魅力は、MVVMのコンポーネント間のインターフェースを統一できること、またアプリ固有の非同期処理や入力処理を統一的なインターフェースでラップできるフレームワークとして使えることです。 自分が知る限りでは上記のことをやるならReactiveCocoaが一番よくできていてまた開発も活発だと思います。

@ikesyoさんに懇親会で聞いた限りだと、Swiftブランチは実運用にはもうちょっと時間がかかりそうだなという印象です。でも粛々と開発は進んでいるようなので期待。

終わった後、同日開催のiOS/Swift勉強会@ヤフーをピザとビール片手にみんなで眺めたというのもちょっと新鮮で楽しかった。 会場の迷惑にならないので思う存分ワイワイできるw

freee社で50人規模の勉強会を開催するのは初めてだったのですが、当日欠席する人も思ったより少なくてよかった。これをきっかけにReactiveCocoaの導入事例が増えるといいなあ。

大阪から駆けつけてくれた @ikesyoさん、主催の @ninjinkunさん、社内でいろいろ調整してくれた @yonekawaさん、トップバッターで話してくれた @tinpayさん、LT発表者の皆様ありがとうございました!

f:id:yo_waka:20141112031225j:plain

f:id:yo_waka:20141112031242j:plain

@ymrl 写真拝借しました。当日はありがとうございました。

iOSアプリの全てのビューコントローラーにGoogleAnalyticsを一括で設定する

今作っているアプリで、改善のためにどれくらい画面が使われているか知りたかったので、GoogleAnalyticsを入れたときのメモ。

GoogleAnalyticsはご存知みんな知っているアクセス解析ツール
iOS用にもSDKが公開されていて、CocoaPodsを使っていればpod installで簡単に入れられる。

pod 'GoogleAnalytics-iOS-SDK', '~> 3.0'

画面の閲覧回数を取るためには、2つやり方がある。

1つは、GAITrackedViewControllerクラスを継承したUIViewControllerを作る。
viewDidLoadなどでscreenNameに画面名をセットしておくと、viewDidAppearで自動でトラッキングリクエストが送信される。

@interface SampleViewController : GAITrackedViewController
@end

@implementation SampleViewController

- (void)viewDidLoad
{
    self.screenName = @"画面名";
}

end

もう1つは、ビューコントローラーのviewDidLoadかviewDidAppearで、GAITrackerクラスを使って画面名を送ってやるやり方。
こっちはWebブラウザ版の使い方に近い。
UITableViewControllerなどUIViewControllerのサブクラスを使っている場合は、こっちでやるしかない。

- (void)viewDidAppear
{
    [super viewDidAppear];

    id tracker = [[GAI sharedInstance] defaultTracker];
    [tracker set:kGAIScreenName value:@"画面名"];
    [tracker send:[[GAIDictionaryBuilder createAppView] build]];
}

つまり、UITableViewControllerをふんだんに使っていたり、UIViewControllerを継承したベースクラスを作っていると、1つ1つのビューに同じ処理を書かないといけない。
これは絶対入れるの忘れそうなのでなんとかしたい。。。と思って調べてみた。

Objective-CにはMethod Swizzlingという、すでに実装されているクラスのメソッドを自前のメソッドに入れ替えるやり方が用意されているらしい。
"objc/runtime.h"が提供している、method_exchangeImplementations関数を使えばクラスメソッドの入れ替えが可能になる。

これを使ってUIViewControllerのメソッドを入れ替えれば各画面ごとにアナリティクス処理を書かずに済みそう。
つまり、UIViewControllerのカテゴリ拡張を作って、viewDidAppearをGATrackerの処理を追加してものに入れ替える関数を用意する。
画面名には「NSStringFromClass([self class])」でクラス名を自動でセットしてやる。

#import <objc/runtime.h>

@implementation UIViewController (GAInject)

- (void)replacedViewDidAppear:(BOOL)animated
{
    // 元のメソッド(名前は既に置き換わっているので注意)を呼び出す
    [self replacedViewDidAppear:animated];
              
    [[GAI sharedInstance].defaultTracker set:kGAIScreenName value:NSStringFromClass([self class])];
    [[GAI sharedInstance].defaultTracker send:[[GAIDictionaryBuilder createAppView] build]];
}

+ (void)exchangeMethod
{
    [self exchangeInstanceMethodFrom:@selector(viewDidAppear:) to:@selector(replacedViewDidAppear:)];
}

/**
 メソッドの入れ替え
  */
+ (void)exchangeInstanceMethodFrom:(SEL)from to:(SEL)to
{
    Method fromMethod = class_getInstanceMethod(self, from);
    Method toMethod   = class_getInstanceMethod(self, to);
    method_exchangeImplementations(fromMethod, toMethod);
}

@end

AppDelegateでこいつを呼び出してUIViewControllerのviewDidAppear関数を入れ替える。

#import "UIViewController+GAInject.h"

@implementation SampleAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // UIViewControllerのメソッド差し替え
    [UIViewController exchangeMethod];
}

@end

これで、UIViewControllerを継承しているUITableViewControllerや自作ビューコントローラーでも、自動でトラッキング処理が走るようになります。
method_exchangeimplementations、Rubyのalias_method感覚で使えるヒッジョーに面白い仕組みですが、そのクラスと子クラスすべての挙動が変わるので使いどころには要注意ですね。

OctSurfer 1.0.1 リリース

リポジトリ一覧でリポジトリのdescriptionが空の場合(なぜか空文字でなく、NSNULLになるケースがあったため)にアプリが落ちてしまう問題と、一覧をスクロールすると下側のスペースが空いてしまっている問題を解消しました。

iTunes Store - OctSurfer 1.0.1


不具合報告していただいた@さんありがとうございました!


UIWebViewでローカルのHTMLを扱うときにハマったこと

ちょいいくつかハマったので。


UIWebviewでローカルHTMLを読み込んでJSを実行しようとしても実行されないときがある

ローカルコンテンツでもUIWebViewDelegateのwebViewFinishLoadでJavaScriptを実行しないと、確実にHTMLのロード後にJSを実行できない。


ローカルで読み込んだJS,CSSファイルのパスの設定の仕方

シミュレータにインストールされるアプリを見てみると、パッケージディレクトリの直下に展開されるよう。
XCode4.5では以下のような指定でおkでした。

NSString *path = [[NSBundle mainBundle] pathForResource: @"foobar" ofType: @"html"];
[webView loadRequest: [NSURLRequest requestWithURL: [NSURL fileURLWithPath: path]]];

あとはHTML内で

<link rel="stylesheet" type="text/css" href="./main.css" />
<script type="text/javascript" src="./application.js"></script>

相対パスでURL指定すれば読み込めます。

OctSurfer という GitHub上 のソースコードを眺めるための iPhone アプリをリリースしました

先月から Objective-C を触り始めて、もっと理解を深めるためにはアプリを作ってみるのが一番早いよなということで、前々から自分が欲しいと思っていた GitHub のビューアーを作ってみました。
おそらくエンジニアとデザイナーさんくらいしか使わないだろうアプリですが、初めての iPhone アプリリリースだぜイエーイ

アプリ名は「OctSurfer」としました。


これは何?

GitHub に公開されているリポジトリソースコードを眺められる iPhone アプリです。
OctSurfer は App Store からインストールできます。無料です。
App Store - OctSurfer


できること

  • 検索 or 自分のスター or 自分が所属する Organizations からリポジトリソースコードをシンタックスハイライト付きで閲覧できます
  • 見ているリポジトリにスターを付けられます
  • 自分のスター画面からスターを削除できます

GitHub 見てる感を出したかったので、ツリービューは本家の挙動となるべく合わせてみました。

注意点としては、GitHub のアカウントを持ってないと使えません><
先月の半ばくらいに突然 GitHub の API の利用制限が始まったため、あわててOAuth2ログイン作りました。
アクセストークンがない API コールは 1 時間 60 回に制限されたようです。アクセストークンがあれば 1 時間 5000 回。


なぜ作ったか

最近は新しいことをやるとき GitHub で検索してコードスニペットを見て参考にすることが増えたし、GitHub で最近勉強してることに近いプロジェクト検索してみたりスター付けてチェックして眺めることも増えました。
でも MobileSafari で GitHub 見るの結構しんどいですよね。。。通勤電車とかだと電波も途切れがちですし。
ちなみにウチの近くの喫茶店はなぜか電波入りません><
かといってダウンロードしたソースコードiPhone に入れてビューアーアプリ入れて見るとかめんどくさすぎる。

GitHub のビューアーアプリはいくつか App Store にあるのですが、探したところリポジトリ内のソースコードを見れるアプリがほとんどなかったんですよね。
2 つくらい見つけたけど機能てんこもりなせいでナビが分かりづらかったり(しかも1つは700円!高すぎワロタ)
GitHub は API 充実してることだし、ソースコードの閲覧をメインのユースケースにしたアプリを自分で作ってしまえと。


iPhone アプリを初めて作ってみた印象

実装的にはいくつか自前の UIView 作ったり、WebView 使って OAuth2 とか国際化とか、API キャッシュとして CoreData 使ったりと、1 通り API を使った iPhone アプリ開発の基本的なところには触れられた気がします。
逆に言うと、これくらいの小さなアプリでも結構コードを書かないといけない。
なので、どうしても作り込まざるを得ない UIViewController 以外は出来るだけ実績のあるライブラリに任せて使っていかないと工数がかさみそう。

ARC + Modern Objective-C は結構書きやすい。昔は冗長だわーと思ってたけどいつの間にか簡潔になったんですね(ただし iOS5 以降に限る)。
あと、XCode の補完が素晴らしい。確かに関数名長いから横長にはなっちゃうけど補完のおかげでタイプする量は少ないしあまり気にならなくなった。
シミュレータの起動も早いし、v4.5 のせいか突然落ちることも無かったです。
AppCode も試してみたい。

そして気になる Waiting for review の期間ですが、土日を除いて 5 営業日でした。土日を入れるとちょうど 1 週間。
In review になってから Ready for sale までは 5 時間くらい。
特に変わった機能使ってるわけでもないのでリジェクトの心配はあまりしてませんでした。


というわけで

UI 考えたり作るのってやっぱり楽しいですね!
自分でも使うし、せっかく出したので、アップデートはしていこうと思っています。
機能を増やすというより、ソースコードビューで定義ジャンプとか行番号出したり使い勝手の改善をしようかなーと思っていますが、もし使ってみてこんな機能が欲しいという方がいたら、右下の「フィードバック」から要望ください!

よかったらぜひぜひ使ってみてください!