「クラウドxスマフォ時代のRuby on Rails入門」セミナーで使ったコードをGitHubに置きました

昨日、行われた クラウドxスマフォ時代のRuby on Rails入門 セミナーで使ったコードをGitHubに置きました。
簡単な Ruby on Rails で作ったサーバーと連携できる iOSアプリです。













コードの特徴

Rails側は

  • CarrierWaveを使った画像アップロート・サムネール付き
  • Deviseを使った認証
  • デザインはTwitter Bootstrap
  • iOSアプリの認証(basic認証)
  • JSONでの画像受け取り
  • Herokuへデプロイ出来ます
  • モデルにRSpecが書いてあります

iOS側は

  • UICollectionView
  • UIImagePickerController
  • NSRailsを使ったRailsとの通信

などです。

NSRailsを使ってみた

7月1122日に行う 「クラウドxスマフォ時代のRuby on Rails入門」 セミナーで使うデモアプリを作るために、NSRails を使ってみました。


概要

NSRails の使い方は https://github.com/dingbat/nsrails に書かれているように、iOS側に Rails と同じモデルを用意し、 モデルクラスの 取得(remoteAll: , remoteObjectWithID: ...)やCRUD(remoteCreate: , remoteUpdate: , remoteDestroy: ...) メソッドを呼び出すだけで、Railsサーバーとのデータやり取りが出来ます。また通信は、同期、非同期をサポートしています。

使ってみて分かったこと

1. Pod は GitHub から

Railsサーバーが起動してないときにiOS側で通信するとへんなエラーで落ちますが GitHubのmasterでは修正されているのでPodfileは以下のようにした方が良いでしょう。

  pod 'NSRails', :git => 'https://github.com/dingbat/nsrails.git'
2. autogen/generate がモデルの雛形を作ってくれて超便利

NSRailsのGitHubにある autogen/generate コマンドで Railsプロジェクトのパスを指定すると、そのプロジェクト内の全モデルに対応する iOS側のモデルの雛形を作ってくれて、超便利です。

./autogen/generate RailsProject
Making directory RailsProject.gen/
Writing files to /Users/yy/tmp/nsrails/autogen/RailsProject.gen
  + Category.h
  + Category.m
  + Customer.h
  + Customer.m
   ...
3. iOS側モデルは必ずしもRailsのモデルと同じで無くても良い

Rails側とiOS側での処理分担は思案のしどころですが、必ずしも一致される必要はありません。iOS側モデルは Rails側のview (〜.json.jbuilder) と対応していれば良いので、iOS側を簡単にしたい場合はviewでたくさんの情報を作り、iOS側はそれを利用する事もできます。今回作っているデモアプリは下のように、簡単な処理も Rails 側でやって渡しています。

json.array!(@timelines) do |timeline|
  json.extract! timeline, :id, :user_id, :caption, :created_at
  json.name timeline.user.name
  json.title photo_title(timeline)
  json.photo_url full_url(timeline.photo.url)  if timeline.photo.present?
  json.photo_thumb_url full_url(timeline.photo.thumb.url)  if timeline.photo.present?
end
  • TimeLine.h
#import <NSRails/NSRails.h>

@interface Timeline : NSRRemoteObject
@property (nonatomic, strong) NSString *caption;
@property (nonatomic, strong) NSNumber *userId;
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *photoUrl;
@property (nonatomic, strong) NSString *photoThumbUrl;
@property (nonatomic, strong) NSString *imageData;
@property (nonatomic, strong) NSDate   *createdAt;
@end
4. 戻さなくて良いプロパティー(インスタンス変数)が指定出来る

NSRailsのCookbookiOS側モデルに付いて色々と書かれています。 shouldSendProperty: メッソドをオーバーライトして、iOSからRailsには戻さないプロパティーを指定できます。これを使い、3. のように余計な情報をRailsから送ってもらってる場合は、それらをshouldSendProperty: に指定しておけば、通信コストを小さくできます。

  • TimeLine.m
#import "Timeline.h"

@implementation Timeline

- (BOOL) shouldSendProperty:(NSString *)property whenNested:(BOOL)nested
{
    if ([property isEqualToString:@"title"] || [property isEqualToString:@"name"] ||
        [property isEqualToString:@"photoUrl"] || [property isEqualToString:@"photoThumbUrl"])
        return NO;
    return [super shouldSendProperty:property whenNested:nested];
}

@end

その他、プロパティー名をRails側と変えたいとか、型を変えるとか・・・ 色々な事ができますので NSRailsのCookbook は一読しておくと良いと思います。

5. 画像データをJSONで送りたい

今回のアプリでは、iOS側から画像データをRails側に送る必要があるので、StackOverflow 等を調べ以下のようなコードを書きました。

iOS側は画像データをBase64エンコードして送ります。

    ....
    Timeline *newTimeline = [[Timeline alloc] init];
    newTimeline.caption = post.caption;
    newTimeline.imageData = [self base64FromImage:post.image];
    [newTimeline remoteCreateAsync:^(NSError *error) {
        if (error) {
            NSLog(@"+++ err %@", error);
        } else {
            NSLog(@"+++ ok : id=%@", newTimeline.remoteID);
        }
    }];
    ....

- (NSString *)base64FromImage:(UIImage *)image
{
    return [UIImagePNGRepresentation(image) base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
}


Rails側は、image_dataの送られて来たBase64エンコードされた画像データをデコードし StringIOで受け取り、その IO Stream を file_field に対応する photo フィールド に代入しています。この後の処理は Carrierwave で行っています。Carrierwaveの処理では IO Stream に path メソッドが必要なようなので、特異メソッドとして追加してます。

ネット上にあるサンプルコードでは Tempfileクラスを使って、テンポラリーファイルに書くコードを多く見かけましたが、それほど大きな画像は送られてこないので、ファイルを作らない StringIO を使ってみました。

  def create
    @timeline = Timeline.new(timeline_params_with_image_data)
    if @timeline.save
      .....

    def timeline_params_with_image_data
      parameters = timeline_params
      if params[:timeline][:image_data]
        parameters[:photo] = parse_image_data(params[:timeline][:image_data])
      end
      parameters
    end

    def parse_image_data(image_data)
      tempfile = StringIO.new(Base64.decode64(image_data))

      def tempfile.path
        "/tmp/upload.png"
      end

      ActionDispatch::Http::UploadedFile.new(tempfile: tempfile, content_type:'image/png', filename:'iphone.png')
    end
6. Basic認証

Rails側でiOSアプリを認証するの部分は、今回は実装が簡単な Basic認証を使ってみました。生パスワードを送っているので実際のアプリで使う場合は SSL 通信が必要ですね。

NSRailsには Basic認証用ヘッダーを送る機能を持っているのでNSRConfigに ユーザー、パスワードを設定するだけです。

    [NSRConfig defaultConfig].appUsername = login.email;
    [NSRConfig defaultConfig].appPassword = login.password;

Rails側は 定番の devise を使っています。コードは deviseのHow To にあったものです。

class TimelinesController < ApplicationController
  before_action :authenticate

  ....

  private
    ....
    def authenticate
      if from_iphone?
        authenticate_or_request_with_http_basic do |username,password|
          resource = User.find_by(email: username)
          if resource && resource.valid_password?(password)
            sign_in :user, resource
          end
        end
      else
        authenticate_user!
      end
    end
end
7. ログの制御

デフォルトではコンソールに通信内容も表示されます、画像を送ったりすると大変な量のログが表示されるので、ログを制限した方が良いと思います。手っ取り早いのは NSRailsの NSRails.h ファイルの以下の定義を変える事です

//					As undefined, NSRails will log nothing
// #define NSRLog 1	//As 1, NSRails will log HTTP verbs with their outgoing URLs, as well as any server errors
#define NSRLog 2	//As 2, NSRails will also log any JSON going out/coming in
8. 通信中表示

NSRConfigのmanagesNetworkActivityIndicator を YES に設定すると通信中にステータスバーに 通信中のグルグルを表示してくれるので便利

[NSRConfig defaultConfig].managesNetworkActivityIndicator = YES;


どんな物が出来たか興味のあるかたは 「クラウドxスマフォ時代のRuby on Rails入門」 セミナー にご参加下さい。転職・人材系会社でのセミナーですが・・・あまり気にしなくて良いと思います。

Cocos2d-xのサンプルコードを試すさいに注意すること!

わけあって Cocos2d-xを勉強しています、入門書を買いサンプルコードをダウンロードしたのですが、コンパイルエラーや画像(Sprite)の大きさがおかしい問題に2日間も悩まされましたが、やっと解決出来たので書いておきます。

Cocos2d-xのバージョンは v2.2.3 、開発環境は Mac OS 10.9.2です。

http://www.cocos2d-x.org/images/orgsite/logo.png

サンプルコードを動かすまでの一般的な手順

  1. Xcode をインストール (Xcode5.1 を使いました)
  2. Cocos2d-x (v2.2.3) を http://www.cocos2d-x.org/download からダウンロード、適当なフォルダーに展開
  3. 下のように create_project.py でプロジェクトを作成、このプロジェクトは HelloWorld画像を表示できる雛形です
  4. Xcode出来たプロジェクトをオープンしビルド、 HelloWorldが表示される事を確認
  5. 入門書のサンプルコードをダウンロード
  6. 上で作ったプロジェクトの Classes内のファイルを消し、書籍のサンプルコードの Classes 内のファイルをコピー
  7. 上で作ったプロジェクトの Resources に書籍のサンプルコードの Resources 内のファイル、フォルダーをコピー
% cd cocos2d-x-2.2.3
% cd tools/project-creator
%  ./create_project.py -project SampleGame -package com.MyCompany.SampleGame -language cpp

ところが・・・・

  • 4. でコンパイルエラー! → Classes内のヘッダーファイルが見つからないエラーなので、ヘッダーサーチーのパスを追加
  • 5. でXcode上でファイルがコピーできない!→ ターミナル上で cp でコピー
  • サンプルーコードが動き出したのですが、なぜか画像やSpriteの大きさが本来の画像より大きかったり小さかったり・・・・ ???

これから2日弱の格闘が・・・

注意点

Macのディスクのフォーマットが 大文字/小文字を区別する場合の注意

Macのディスクはフォーマット時に Mac OS 拡張(大文字/小文字を区別、ジャーナリング を選択すると unixのように大文字と小文字を区別します。Unix系のツールを多く使う人はこの方が幸せになれます。たぶんデフォルトは区別しないファイルシステムです(ただしMac OS が工夫して一見 区別してるように振る舞っています)。

create_project.py が作ったプロジェクトのClassesグループは実は下の画像のように 小文字 classes フィルダーリンクしています!

viなどで SampleGame.xcodeproj/project.pbxproj を直接開き、../classes を ../Classes に書き換えたら 4, 5 の問題が解決できました。
project.pbxproj は テンプレートの template/multi-platform-cpp/proj.ios/HelloCpp.xcodeproj/project.pbxproj を変更してしまうのが良いと思います。

追加: pull request がマージされたので次ぎのバージョンでは直ってると思います。

Resources(リソース)のコピーは Create folder reference for any added folders を使う!

画像やSpriteの大きさが本来の画像より大きかったり小さかったりする問題の解決方法は、Cocos2d-x:絶対にわかるマルチ解像度(マルチディスプレイ)対応 に書かれていました。

cocos2d-x はいろいろな解像度のデースプレーに対応出来るように複数の解像度のリソースを用意し解像度により切り換える仕組みを持っています。サンプルコードにも複数の解像度用のリソース(画像)がフォルダー(例えば hd, sd)を分けて用意されています。

7. の Resources のコピーの際には何気なくXcodeの Create groups for any added folders を使っていました。この設定の場合、解像度別のフォルダーはXcode上ではグループになります(ディスク上ではフォルダーになっていますが)。これをiOSシュミレータにインストールするとフォルダーは無くなり、1つの解像度のファイルのみコピーされてしまいます (>_<)ゞ

まとめ

ということで、一般的な手順で無事にサンプルコードが動くようになりました。

今回参考にした書籍は

cocos2d-xによるiPhone/Androidアプリプログラミングガイド (for Smartphone Developers)

cocos2d-xによるiPhone/Androidアプリプログラミングガイド (for Smartphone Developers)

Cocos2d-x by Example Beginner's Guide

Cocos2d-x by Example Beginner's Guide

とても良い本です。Cocos2d-x by Example Beginner's Guideのサンプルは2.0用ですが 2.1用のアップデート情報が http://rengelbert.com/blog/cocos2d-x-book-update/ ここにあり、2.2.3 でも動きます(全て試したわけではありませんが)。

Xcodeでプロジェクトにドラッグ&ドロップでファイルを追加する場合の注意点

昨日の「出版記念! iPhone開発初心者向け無料セミナー」参加者のみなさま、参加ありがとうございました。

ハンズオンで、音声ファイルをプロジェクトに追加する際には、File → Add Files to "..." を使った場合に表示されるダイアログで、 ■ Copy items into destination group's folder (if needed) をチェックして下さいとお伝えしましたが、

ドラッグ&ドロップでファイルをプロジェクトに追加した際に表示されるダイアログは下の様で、最初は Add to targets □ がチェックされていません。ドラッグ&ドロップ した場合は、これもチェックして下さい。
Add to targets がチェックされて無い場合には、ターゲット(iPhone,iOSシュミレーター)にはそのファイルはコピーされません。

ちなみに、Add to targets がチェックされて無い場合は Build Phases タブの Copy Bundle Resources でファイルをターゲットに追加できます。

Push Notificationに関連する証明書の作成手順

先日、お客様にiOSのPush Notificationを説明するためのサンプルコードを作ったのですが証明書の作成でハマッたので手順を図にしました (下の画像をクリックすると大きな画像が表示されます)


正確な作成手順は Local および Push Notification プログラミングガイド に書かれていますが、いくつものファイル(証明書)が出てくるので理解しにくいので図にしてみました。

iOSのPush NotificationのプログラミングはiOS側もサーバー側も簡単です。一番難しいのは証明書の作成だと思います。証明書が間違っていても、Push Notificationの中継を行うAppleのサーバーからはエラーが戻らないので、かなり悩む事になります。


ちなみに今回作ったのサンプルでサーバー側は Grocer を使いましたが、なかなか良い感じでした。

書籍「はじめてのiPhone/iPadアプリ開発」をプレゼントします

iOSアプリ開発の本を書きました! 著者用の献本を何冊か頂きましたので、ブログに書評を書いてくれる方にプレゼントいたします。

はじめてのiPhone/iPadアプリ開発―iOS6/Xcode4対応版 (TECHNICAL MASTER)

はじめてのiPhone/iPadアプリ開発―iOS6/Xcode4対応版 (TECHNICAL MASTER)

この本は、JavaRuby,PHPなどのプログラム経験はあるけど C言語を知らない方が、 iPhone/iPadアプリを開発する事を想定した教科書的な本です。

iOSアプリを勉強したい方で書評を書いてもよい方は、 yy@ey-office.com にメール下さい。なお、発送は来週初めになると思います。

応募は締め切りました、たくさんのご応募ありがとうございます !!

プレゼントに当選された方にはメールいたしました。
メールが届かなかった方、すいませんが書店や アマゾンで買って下さい m(_ _)m

よろしくお願いいたします

私以外にも一名の利用者がいることが分かった Tweetなう がバージョンアップしました!

私以外にユーザーはいないと思われていたiPhoneアプリ Tweetなう ですが、昨年秋、ある方からバージョンアップのリクエストを頂きました。

それから、だいぶ経ってしまいましたが、やっとiOS6iPhone5に対応した Tweetなう がリリース出来ました。

今回のリリース内容は、

です。ソースの方も引き続き GitHub で公開してます。