blue_field

技術的なメモとか読書記録とかいろいろ(の予定

会社でiOSエンジニアになった

更新をサボっていたら、もう1年くらい経ってしまっていた。 ということで、久々に投稿してみるテスト。。

この4月、いま務めている会社に新卒で入ってちょうど5年目になった。 未経験でエンジニアになって、それ以来 Railsをメインで書いていたのだけれど(あと片手間でのJavaScriptも...)、 半年ちょっと前から役割が変わり、iOSアプリを会社で作るようになった。 そんなわけで、最近の開発タスク時間の8, 9割はSwiftを書いている。

作っているのはこれ。

アプリをメディアに取りあげてもらったりもした。


RubyMotionで簡単なアプリを作ったことはあったものの、SwiftおよびXcodeで開発するのは完全に初めて。 社内にもアプリ開発のノウハウが全くなくて結構苦労したのだが、無事リリースできたときは本当にホッとした。

プロダクトマネージャ、サーバサイドエンジニア、デザイナー、自分、の4人チームでずっとプロダクト開発しているが、 開発期間3ヶ月弱(企画・プロトタイピング期間を含めても4ヶ月ちょいくらい?)でリリースできたのは、 悪くないスピードだったんじゃないかと思っている。
(※他社の人たちがどれくらいのペースで開発してるのかあまり知らないので、これが速いのか遅いのか実のところよく分からないけれどw)

リリースも済んで、今はアプリをブラッシュアップしながらグロースさせていく時期。 そして、さらにその先の未来像をチームで話しながら描いているところ。

役割はまた変わったりすることもあるだろうけど、プロダクトとそこに必要な技術に向き合いながら頑張っていこう、 という感じである。


余談だが、最近読んだこの記事が良かった。 自分のような小規模な会社の開発者には響く(はず)。 オーナーシップを持って事業に臨まないと!と、改めて気が引き締まった。

というわけで、久々のブログ更新終わり。 (もうちょっと更新頻度増やしたい)

RubyMotionでiOSアプリ作ってリリースした

晴れてiOSアプリ童貞を卒業したw

前にも少し書いたのだけど、 週末の空き時間のみを利用して、RubyMotionで小さなアプリを書いていた。 単機能ながらだいぶかかってしまったけど、ようやくリリースまでこぎつけた、ということでエントリ。

PokeBu

PokeBu

  • YUSUKE AONO
  • ニュース
  • 無料

Pocketに突っ込んだ記事を読むアプリで、 はてブのコメントを見たりアプリ内でブックマークできたり記事をアーカイブできたりする。 ただの俺得アプリなんだけど、はてブもPocketもヘビーに使ってる僕と同じような方なら多少役立つかもしれません...
あと、センス無いくせに5分くらいしか時間かけずに作った非常に雑なアイコンなので変えたい

GitHub: ysk1031/Pokebu

モチベーション維持の難しさ

やっぱり週末だけの開発だとモチベーションというのはそう長く続かない。 このアプリも10月くらいから開発しているが、 GitHubのコミットのグラフを見ると、昨年末以降はほとんど触ってなくて、今年の3月に頑張ってやる気出して作りきったのが分かるw

f:id:yusuke_1031:20150506113700p:plain

今回のケースは、単純に自分の欲しいモノを作ろうという「自分の欲しいモノ駆動開発」(?)だったわけだが、 それでも相当強い意志がないとやっぱり飽きるのだなあ、と再確認。 趣味での開発は、1ヶ月くらいで作りきれる分量がやっぱり良いんだろうなあと思う。

今回のアプリも、振り返ってみると書いたコードは大した分量じゃないし、もう少し短いスパンで絶対作れた。 何か作り始めたら他の分野の学習などにはいったん手を出さず、一気に作りきるほうが良いのかも。

リジェクト2回

iOSアプリを出す際に避けられないのがApple様のアプリ審査。 審査ももちろん初体験だったのだが、リジェクトを2回くらった。

1回目は完全な凡ミスで、デモ用のテストアカウント(Pocket・はてなのID & パスワード)を貼り忘れたというもの。 レビュー待ちに8日かかってのリジェクト理由がこれなので、だいぶ萎えたというか自分の無知を呪ったw

2回目は、ログイン周りがユーザー体験的にダメだということでリジェクト。

Upon launching the app, a web page in mobile Safari opens for logging in with a Pocket account, then returns the user to the app. The user should be able to log in without opening Safari first.

上記のような理由。 今回作ったアプリはPocketでのログインが必須で、 その実装にはPocketのSDKを利用している。 このSDKによるログインだが、デフォルトの状態ではPocketのアプリがインストールされてないとSafariに遷移し、 そちらのWeb画面でログインすることになる。 ここがダメで、アプリ内でログインを完結できるようにせよということらしい。 SDKレポジトリのissue見ると同じ問題に当たった人がいて、解決した方法も載っていたので真似した。

iOS App using Pocket-ObjC-SDK is rejected because of OAuth login with Safari. · Issue #9 · Pocket/Pocket-ObjC-SDK

この問題を解決したところ無事審査も通り、ステータスが「Ready for Sale」になった。 3度のレビュー待ちがそれぞれ8日、10日、10日という感じだったので、計1ヶ月くらいレビュー待ってたことになる。。 個人の趣味で作ってるアプリだから良いんだけど、ビジネスでやってるアプリで同じようなこと起きたら結構な損失ですね😓 しっかり準備してレビューに臨むべきだった。

Pocketログインの修正

上記のログイン部分の修正部分のメモ。 ログインでSafariに飛んでしまうのを無理やり修正する。

UIApplicationクラスを継承したクラスを作成して、openURLメソッドをオーバーライド。 getpocket.comを開こうとした時のみ、NSNotificationCenterを利用してアプリ内で通知を送信する。

class CustomUIApplication < UIApplication
  def openURL(url)
    if !url.host.nil? && url.host.isEqualToString("getpocket.com")
      notificationCenter = NSNotificationCenter.defaultCenter
      notificationCenter.postNotificationName("PokebuOpenPocketAuthNotification", object: url)
      return
    end

    super
  end
end

Pocketログインボタンを表示するコントローラでは、上の通知のオブザーバーを追加し、通知が来た時に実行されるメソッドを作成。 SVWebViewControllerを利用して、Webビューのモーダルを表示。

class IntroViewController < UIViewController
  def viewDidAppear(animated)
    ...
    
    # NotificationCenterにオブザーバー追加
    notificationCenter = NSNotificationCenter.defaultCenter
    notificationCenter.addObserver(self,
      selector: 'openPocketAuth:',
      name:     "PokebuOpenPocketAuthNotification",
      object:   nil
    )
  end
  
  # 通知が来たら
  def openPocketAuth(notification)
    webViewController = SVWebViewController.alloc.initWithAddress(notification.object.absoluteString)
    @pocketAuthNaviController = UINavigationController.alloc.initWithRootViewController(webViewController)
    self.presentViewController(@pocketAuthNaviController, animated: true, completion: nil)
  end
end

オーバーライドしてしまったので、Rakefileinfo.plistを変更。

# Rakefile

Motion::Project::App.setup do |app|
  ...
  
  app.info_plist['NSPrincipalClass'] = 'CustomUIApplication'
  
  ...
end

これでアプリ内でログイン出来るようになった。

RubyMotionについて

やっぱりRubyで書けるのは心地よくて、RubyMotion好きである。 CocoaPodsのライブラリも普通に使えるし、 iTunes ConnectへのアップロードなんかもHipByteがライブラリ出してて簡単に出来た。 気軽に作れて非常に良い。

一方で、Swiftがスタンダードになっていく時流の中で敢えて使い続ける理由も無いかなとは思っていて、 今後個人で別のアプリを作ろうとなったときにどうするかは結構悩む、Objective-Cほど抵抗もないし。。 Androidアプリも書けちゃうし、魅力的ではあるのだが。

今回に限って言えば、Cocoa touchの部分の習得(UIKitフレームワークとか)に集中できたので、RubyMotionで良かったかなと思っている。 慣れてない別の言語で書き始めると、その言語の記法が分かんなくて詰まったりするので。。

おわり

そんなわけでアプリを作ってみての簡単な振り返り的なやつでした。 超基礎的なUIを使っただけだし、iOSマスターしたなんて口が裂けても言えないけど、 アプリの作成・審査・リリースまでの流れを自分で体験できたのは非常に良かった。

会社の技術陣のポートフォリオも例えば1年前と比べると少しずつ変わっていて(増えていて)、 ようやくネイティブアプリの分野が入ってきて、遅まきながらモバイル対応が始まった感がある。 Swift書けて僕より全然iOSに詳しい同期もいるし、1年目ながらアプリデザインできる子もいる。 自分のポジションとしてモバイル方面にどこまでコミットしていくべきなのか定めてないが、 機会があるのに何もできないのも嫌なので今後も触れていきたいところではある。 (会社でやるなら間違いなくSwiftになるので、要習得)

アプリのLP作る気力が全く沸かなくて困る。それでは今回はこのへんで。

Reactに入門

流行に乗っかって、Reactを学び始めている。

f:id:yusuke_1031:20150503225826p:plain

A JavaScript library for building user interfaces | React

といってもまだほんの少し。 概念を分かりやすく説明してくれているスライドが最近いくつか出ているのでそれを読み、 公式のチュートリアル & Web+DB vol.86 のReact解説記事を見ながら写経したくらい。

WEB+DB PRESS Vol.86

WEB+DB PRESS Vol.86

あと、一人React.js Advent Calendar 2014 - Qiitaをちょこちょこ見てる。 (分かりやすい記事を一人で1ヶ月分書いてらっしゃってほんとにすごいです。。)

会社のプロダクトのjQueryゴリゴリのコードの管理に限界を感じていて、 Vueなどのフレームワークも触ったりしたのだけど、より面白いアプローチで問題解決するライブラリが出てきたなという印象。 当然ながらコードの見通し・管理という面では大きなメリットがあるので、 もう少し触って、うまくやれそうならリプレイスしていきたいなーなんて考えている。

というわけで、Web+DBの記事の備忘録というか頭の整理目的でメモ。 naoya_itoさんの解説がいつもの如くすごく分かりやすい。

ReactはViewのみ提供する

AngularなどのMVCフレームワークとは異なり、ReactはViewのみを提供する。フルスタックではない。 Reactのコンポーネント外のデータモデルで状態管理をしたくなったら?というところで、Facebookの提唱するFluxアーキテクチャが登場する、と。

Flux | Application Architecture for Building User Interfaces

(まだFluxには全く手を出してない😓)

サンプルコード1(Markdownプレビュー)

以下の様なReactのコードで、Markdownを入力するとリアルタイムでプレビューされるやつができる。

<!--  markdown.html -->

<html>
  <head>
    <title>Markdown by React</title>
  </head>
  <body>
    <div id="app-container"></div>
    <script src="build/markdown.js"></script>
  </body>
</html>
// src/markdown.jsx

var React = require('react');
var marked = require('marked');

var App = React.createClass({
  getInitialState: function() {
    return { markdown: "" };
  },
  updateMarkdown: function(e) {
    this.setState({ markdown: e.target.value });
  },
  render: function() {
    var html = marked(this.state.markdown);
    return (
      <div>
        <textarea onChange={this.updateMarkdown}></textarea>
        <div dangerouslySetInnerHTML={{__html: html}}></div>
      </div>
    );
  }
});

React.render(
  <App />,
  document.getElementById("app-container")
);

画面(markdown.html)

スクショ。

f:id:yusuke_1031:20150503225436p:plain

(上部のテキストボックスにMarkdownを書いてくと、下部にリアルタイムでプレビューが出る)

コンポーネント

Reactでは、UIの要素をコンポーネントに分割して、それを組み合わせることでUIを構築する。 コンポーネントは、React.createClassrenderメソッドを持たせて作成する。 上の例では、Appという名前でたった1つのコンポーネントを作成している

コンポーネントレンダリング

React.renderで行う。 サンプルコードでは、指定したDOM要素(#app-container)にAppコンポーネントレンダリングしている。 このように、コンポーネントはHTMLのタグっぽく書くと参照され、参照されるとrenderメソッドが呼ばれる。

Appコンポーネントrenderでは、textareadivの2つの要素が入ったdivreturnする。 画面のスクショの通り、この2つの要素が、 Markdownを入力するテキストボックスとそのプレビューを表示するエリアとなる。

JSX

JavaScript syntax extension。 JSの中に直接HTMLタグを記述してる箇所がJSXの記述。 ちょっと気持ち悪いと最初だけ思ったけど、すぐ慣れた。 このJSX形式のファイルを通常のJSに変換してHTMLファイルで読み込む。

JSXのコードは以下のように変換される。 変換後のJSの中にReact.createElementというのがあるが、これは名前の通り(Reactの)要素を作る。 このJSコードをJSXでは直感的に書けるようにしてくれている。

// jsx

<div>
  <h1>Hello, React</h1>
</div>
// js

React.createElement("div", null,
  React.createElement("h1", null, "Hello, React") 
)

ちなみにDeNAが出してるやつかと勘違いしてた、全く別物だった

JSX | XML-like syntax extension to ECMAScript

State

Reactにおけるコンポーネントは、StateProps という2つの要素(インターフェース)から成り立っているのだが、まずStateについて。

Stateはその名の通り「状態」を表し、コンポーネントの状態管理を行う。 サンプルコードだと、テキストボックスに入力される文字列が状態を持った値(State)。 これをReactで管理する。

Appコンポーネントの中でgetInitialState()というのがあるが、 これが状態の初期値を返すためのコールバック。 Stateを表すmarkdownという値が空であることを指定している。

また、同じくAppコンポーネントupdateMarkdownプロパティの中にthis.setState()という記述がある。 このsetState()で状態の更新を行う。 サンプルコードでは、textareaonChange属性にこれをセット。 イベントハンドラを指定することでテキストボックス内の変更イベントを監視し、 変更の度にupdateMarkdownの中で指定した関数が呼び出され、状態が更新される。 つまり、テキストボックスへの入力値がmarkdownに渡される。

そしてここがReactの特徴の一つ。 コンポーネントの状態に変更があると、コンポーネントの再描画が自動で行われる。 先にも述べたとおり、Appコンポーネントは、 入力用テキストボックスとそのプレビュー表示エリアを含んだdivを描画するわけだが、 このレンダリングがテキスト入力(コンポーネントの状態変更)の度に実行される。 コンポーネントの状態更新だけを行えばよく、自分でDOMの操作を行う必要はない。

Virtual DOM

状態更新の度に全て描画するとか効率悪くね?という話になるわけだが、ここで出てくるのがVirtual DOM。 Reactは描画の際に生のDOMを直接操作するのではなく、Virtual DOMというメモリ上の仮想構造体を操作する。 詳しく書かないが、ReactはこのVirtual DOMをまるっと更新して変更前との差分を計算する。 差分を埋める操作だけが本物のDOMに対して行われるため、パフォーマンスがすごく悪いということはない。

コンポーネントの状態を更新すれば、Reactが良きに計らってDOMを更新してくれる。 「このイベントが起きたら、あそこのliの後にもう1個liをappendして...」みたいなjQuery的発想から脱却できる。 変更後の姿を指定してDOMをまるっと更新するように書けば、Virtual DOMの力で差分だけが反映される。 いわゆる「富豪的」な開発スタイルになる。

参考: VirtualDom - なぜ仮想DOMという概念が俺達の魂を震えさせるのか - Qiita

JSXは、Virtual DOM用の要素を生成する記述(React.createElement)だったというわけか。

サンプルコード2(Markdownプレビュー)

さっきのMarkdownプレビューのコードを少し変更する。

// src/markdown.jsx

var React = require('react');
var marked = require('marked');

var App = React.createClass({
  getInitialState: function() {
    return { markdown: "" };
  },
  updateMarkdown: function(markdown) {
    this.setState({ markdown: markdown });
  },
  render: function() {
    return (
      <div>
        <TextInput onChange={this.updateMarkdown} />
        <Markdown markdown={this.state.markdown} />
      </div>
    );
  }
});

var TextInput = React.createClass({
  propTypes: {
    onChange: React.PropTypes.func.isRequired
  },
  _onChange: function(e) {
    this.props.onChange(e.target.value);
  },
  render: function() {
    return (
      <textarea onChange={this._onChange}></textarea>
    );
  }
});

var Markdown = React.createClass({
  propTypes: {
    markdown: React.PropTypes.string.isRequired
  },
  render: function() {
    var html = marked(this.props.markdown);
    return (
      <div dangerouslySetInnerHTML={{__html: html}}></div>
    );
  }
});

React.render(
  <App />,
  document.getElementById("app-container")
);

コンポーネントの組み合わせでUIを構築

Reactでは、UIの要素をコンポーネントに分割して、それを組み合わせることでUIを構築する。 最初のサンプルではコンポーネントは1つだけだったが、App, TextInput, Markdownの3つのコンポーネントを作成した。 「役割」を考えながら分割していけば良いのかな?

TextInputがメッセージを入力するコンポーネント、Markdownが入力値をプレビューするコンポーネント。 Appコンポーネントではこれらをrenderしており、2つを束ねるような存在になっている。

このようにコンポーネントには親子関係を持たせることができる。 今回のケースでは、Appという親にTextInput, Markdownという子がぶら下がっている、ということになる。

Props

Reactにおける、Stateと並ぶもう一つの要素がProps。 Propsは名前の通り「属性」を表し、コンポーネントの属性(プロパティ)を扱う仕組み。

Propsを通して親 => 子に値が渡される。 サンプルコードを見てみると、例えばAppコンポーネントの中に以下のような記述がある。

<Markdown markdown={this.state.markdown} />

これは、子のMarkdownコンポーネントmarkdownというPropsに、 親のAppコンポーネントmarkdownというStateが渡されていることを示す。 Markdownコンポーネントの中でthis.props.markdownという記述があるが、 こうやって書くことでAppコンポーネントから渡された値を参照している。

Propsはimmutableに

原則として、Propsを通して値を受け取った子コンポーネントにおいては、値を更新しない(immutableにしておく)。 子は値を使うのみで、状態の管理は親に任せる。

こうしてデータの扱いを親に集約することで、子側では状態管理を考えなくて済む。 ビューの状態遷移の管理をする際の見通しが良くなる。

子 => 親への状態更新処理移譲

上でも書いたように、子は親のStateを直接更新することはできない。 子コンポーネントがイベントを受け取って状態を更新したい、となったらどうするのか。 こういったとき、親はPropsを使って子にsetState()を含む関数を渡し、 子はその関数を呼び出し引数として値を渡す。 関数は親側で呼び出されるので、Stateの更新処理は親コンポーネントの中に閉じ込めることが出来る。

サンプルコードで説明するとこんな感じだろうか。

  • 親のAppコンポーネントは子のTextInputコンポーネントonChangeというPropsを渡す
  • その値はupdateMarkdownというプロパティ(呼び出すとsetStateを実行)である
  • TextInputコンポーネントは、renderするtextinputタグのonChange属性に_onChangeというプロパティをセット
  • テキストが入力されたらイベントが発火し、_onChangeで指定した関数が実行される
  • その関数の中では、親から受け取ったonChange(=setStateを実行する関数)を呼び出し、引数としてe.target.value(テキストボックスへの入力値)を渡す
  • Appコンポーネントで実際にsetStateが実行され、子が渡した引数がmarkdownの値になってStateが更新される。

サンプルコードの流れ

いまさら。

propTypes

propTypesというプロパティで、親から渡ってくるPropsの型の検証を行える。 上記の例でいえば、TextInput内のonChangeの値は関数、 Markdown内のmarkdownの値は文字列。 Propsとしてどういうインターフェースを公開しているのか明示できるので、書いといたほうが良さそう。

Browserifyとか

特に触れてないけど、Browserify + reactifyを使ってJSXをビルドしている(src/markdown.jsx => build/markdown.js)。 Browserifyを使うの初めてだった。

Reactの説明見てても、Node.js(npm)周りの知識を普通に知ってる前提で説明書いてあったりするし、 普段Rails使ってるとはいえ、こっち側もそろそろ知ってないとマズイなと思った。

おわり

というわけで長くなったけどメモでした。 (基礎的な)概念は掴めたので、あとは実際に書いていくしかないだろうな。 会社のハッカソンで作った小さいRailsアプリがあるので、まずはそのあたりにReactを組み込んでみようかと思う。

Fluxを学ぶべきタイミングはいつなんだろう。 やっぱ作ってるうちにきつくなってきて、ああいうアーキテクチャを使いたくなるものなのかな??

monitで簡単なプロセス監視

会社のサービスのサーバで動いてるプロセス(デーモン)を監視したかったので、monitをお試し導入した。その際のメモ。

Monit

Monit is a small Open Source utility for managing and monitoring Unix systems. Monit conducts automatic maintenance and repair and can execute meaningful causal actions in error situations.

ふむふむ。さらに、

Up and running in 15 minutes!

と書いてある。手っ取り早くプロセス監視を行う手段としては良さそう。

インストール

インストールするサーバは、Amazon EC2 (Amazon Linux)。 yumでインストール可能。

プロビジョニングツールとして最近はItamaeを利用しているので、レシピを書く。

# recipes/monit.rb

package "monit"

service "monit" do
  action %i(enable start)
end

とりあえずこんな感じでItamaeを実行し、monit自身を動かす。

$ monit -V
This is Monit version 5.2.5
Copyright (C) 2000-2011 Tildeslash Ltd. All Rights Reserved.

デフォルトの設定

以下のデフォルト設定ファイルをチェック。

  • /etc/monit.conf
  • /etc/monit.d/logging
  • /etc/logrotate.d

共通設定

ほとんどコメントアウトされている。

# /etc/monit.conf

set daemon 60

include /etc/monit.d/*

set daemonはmoitの監視周期。60秒に1回、プロセスが生きてるかチェックしてくれる。 各プロセス固有の設定ファイルは、/etc/monit.d/以下に書いていけば良さそう。

ログ

# /etc/monit.d/logging

set logfile /var/log/monit
# /etc/logrotate.d/monit

/var/log/monit {
    missingok
    notifempty
    size 100k
    create 0644 root root
    postrotate
        /usr/bin/pkill -HUP -U root -x monit > /dev/null 2>&1 || :
    endscript

}

set logfileでログの出力先を設定 (/var/log/monit)、ログローテートもとりあえずやってくれそうなのでこのままで。

monitコマンド

monitコマンドが使えるようになっている。monit -hでコマンドやオプションが見れる。

例えば、monit summaryで死活監視中のプロセスが見れたり、monit monitor allで設定したプロセスの監視を開始(再開)できたり。

これらのコマンドを実行した際、デフォルトの設定のままだとエラーが出てしまった。

# monit summary
monit: error connecting to the monit daemon

回避するため、下記のように設定を/etc/monit.confに追記した。

# recipes/monit.rb

execute "Update base setting" do
  user "root"
  command <<-EOC
sudo sh << SCRIPT
cat >>/etc/monit.conf <<'EOF';
set httpd port 2812 and
    allow localhost
EOF
SCRIPT
  EOC
  not_if "grep '^set httpd port 2812 and$' /etc/monit.conf"
  notifies :reload, "service[monit]"
end

参考: monit: error connecting to the monit daemon - BTT's blog

# monit summary
The Monit daemon 5.2.5 uptime: 23m

System 'system_hogehoge'          running

redisを監視

試しにredisを監視してみる。

設定

redis用の設定ファイルを作成。こちらもItamaeで。

# recipes/monit.rb

template "/etc/monit.d/redis.conf" do
  mode "644"
  owner "root"
  group "root"
  source "../templates/monit/redis.erb"
  notifies :reload, "service[monit]"
end
# templates/monit/redis.erb

check process redis with pidfile /var/run/redis/redis.pid
  start program = "/etc/init.d/redis start"
  stop program  = "/etc/init.d/redis start"
  if 5 restarts within 5 cycles then timeout

/etc/monit.d/redis.confに、check process ..以下の設定を書き込む。

あんまり見やすくないんだけど、設定の記法のドキュメントがある。 http://mmonit.com/monit/documentation/monit.html

  • 1行目のcheck processで、監視対象のプロセスのpidファイルを指定。
  • 2, 3行目のstart program, stop programで、起動・停止のプログラムを記述。特に何も指定しないとrootユーザーで実行される。ユーザーやグループを指定するときは、start program = "/etc/init.d/redis start" as uid Yusuke and gid Yusukeと書く。
  • 4行目のif文で、特定条件の時のmonitの挙動を指定できる。この場合は、5回の監視中にredisが5回再起動を試みると、timeoutアラートを出してmonitは監視をストップする。

if文が便利そう。

if totalmem is greater than 300MB for 10 cycles then restart

例えば上記のように、10回の監視全てにおいてメモリ使用量が300MBを超えていたら再起動、みたいな使い方もできる。

テスト

monitがちゃんと機能するか試す。

sudo service redis stopでRedisをストップさせると、monitのログに出力が。 自動で再起動を行っているのが分かる。

# /var/log/monit

[JST Mar 31 13:52:41] error    : 'redis' process is not running
[JST Mar 31 13:52:41] info     : 'redis' trying to restart
[JST Mar 31 13:52:41] info     : 'redis' start: /etc/init.d/redis
[JST Mar 31 13:53:42] info     : 'redis' process is running with pid 31814

確認してみると、ちゃんと動いてる。

$ sudo service redis status
redis-server (pid  31814) is running...

先に述べたとおり、何らかの問題があってこの再起動が5回連続で失敗したら、monitは監視をやめてしまう設定になっている。そういった時に監視を再開させるのが、monit monitor allもしくは`monit monitor <process_name>'。

おわり

以上、monitの簡単な導入の備忘録。

会社では、resqueを監視させて非同期処理が滞らないようにしたり、OOMキラーさんがやってきてもsshdが復活できるようにしたり、という使い方で試している。

今は全く使っていないが、アラートメールを送信する機能などもある。シンプルな割にそれなりの機能を持っていて、結構細かい設定まで行える印象。突っ込んだ監視はできないが、導入ハードルがかなり低いのが良いなと思った。

RubyMotionで、Interface Builderを使ってLaunch Screen作成

久しぶりの更新。

年が明けてから仕事がバタバタしていて触れてなかったのだが、今月は久々にRubyMotionを書いていた。Launch Screen(スプラッシュ画面)を作る機会があったので簡単にメモっておく。

Interface BuilderでLaunch Screen作成可能に

どうやらXcode6(iOS8)から、Interface Builderを使って起動画面を作れるようになったらしい。iOS界隈の情報はそんなにキャッチアップしてないので知らなかった。

xib形式で画面を作成できるということは、AutoLayoutを使って1ファイルで全ての機種サイズに対応できるということ。機種ごとにサイズの異なる画像が必要なんだとばかり思っていた情弱の自分には朗報だった。

こちらの記事などに書いてある。
[iOS 8/Xcode 6] Launch Screen (スプラッシュ画面) を Interface Builder で作成できるようになりました | Developers.IO

RubyMotionからInterface Builderを扱う

Xcodeを使ってSwiftで書いている場合は、xibファイルとの連携はすぐに出来る。RubyMotionから同様のことをするには、ibというgemを使用する。

rubymotion/ib

# Gemfile

gem 'ib'

bundleすると、rakeタスクが追加される。
(※インストールされたibのバージョンは 0.7.2)

rake ib:open

を実行。プロジェクトのルートディレクトリにib.xcodeprojというディレクトリが作成され、Xcodeが開く。Xcode上でのディレクトリの構成は以下のようになっている。

f:id:yusuke_1031:20150330015455p:plain

これでInterfaceBuilderとの連携が可能に。

Launch Screen作成

Xcodeの)Resourcesのところで、右クリック -> 新規ファイル作成。ファイルのテンプレートとしてLaunch Screenを選択。新規作成されるxibファイルの置き場所は、RubyMotionプロジェクトのresourcesディレクトリ下にする。 以下の様なそっけないViewがXcode上にできる。

f:id:yusuke_1031:20150330015539p:plain

あとは、このxibファイルがLaunch Screenになるよう、RubyMotion側で設定してあげれば良い。上記で新規作成したファイル名がLaunchScreen.xibであればこんな感じで。

# Rakefile

Motion::Project::App.setup do |app|
  ...
  app.info_plist['UILaunchStoryboardName'] = 'LaunchScreen'
end

これで準備完了。rakeでアプリをビルドする際に、このxibファイルも一緒にコンパイルされ、あのそっけない画面がアプリの起動画面として表示されるようになる。Xcode側で編集・保存をすれば、次のビルド時にそれが当然反映される。AutoLayoutの制約を付けながらパーツを付けていけば、複数の画像を作らなくて済みそう。

おわり

以上、簡単な備忘録。

RubyMotion書いてる時は、基本的にXcode使わずにviewを作ったりしていたが、Xcode側でやるのも良いかなと思った。(今作ってるのはそんなに複雑なものでもないので、コードだけで書けてしまうのだけれども)

ibの説明はちょっと古いがこの記事が良さ気。
第八回 RubyMotion で Interface Builder を使うには? - 実践!RubyMotion - Mobile Touch

いまさら言うことでもないが、Xcodeの各機能のアイコンは本当に分からん、Appleが作ってるとは思えないw