『RailsとiPhoneではじめるアプリケーション開発』を進める時の注意点・ハマったこと Chapter 12
いよいよ最終章!
やっとこの苦しみから解放される!
気を引き締めて行きます!

- 作者: 栗田由菜,丸山弘詩
- 出版社/メーカー: インプレスジャパン
- 発売日: 2013/08/21
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
『RailsとiPhoneではじめるアプリケーション開発』を進める時の注意点・ハマったこと Chapter 03まで - diceK66のブログ
Chapter 04 -> 06
『RailsとiPhoneではじめるアプリケーション開発』を進める時の注意点・ハマったこと Chapter 06まで - diceK66のブログ
Chapter 07
『RailsとiPhoneではじめるアプリケーション開発』を進める時の注意点・ハマったこと Chapter 07 - diceK66のブログ
Chapter 08 -> 09
『RailsとiPhoneではじめるアプリケーション開発』を進める時の注意点・ハマったこと Chapter 08 -> 09 - diceK66のブログ
:token_authenticatableが削除されたので他の方法で実装してみた
『RailsとiPhoneではじめるアプリケーション開発』を進める時の注意点・ハマったこと Chapter 010 - diceK66のブログ
Chapter 10
:token_authenticatableが削除されたので他の方法で実装してみた - diceK66のブログ
Chapter 11
『RailsとiPhoneではじめるアプリケーション開発』を進める時の注意点・ハマったこと Chapter 011 - diceK66のブログ
重要事項
以下の2つは必ず読みながら進めましょう
+ Objective-C - RailsとiPhoneではじめるアプリケーション開発をiOS7向けに読み進めるときの注意・修正点 - Qiita
+ 詳細 -- 栗田 由菜「RailsとiPhoneではじめるアプリケーション開発」Impress - みんなで正誤表
エラーが起きたらコンソールをじっくり読みましょう
特に自分が作成or編集したファイルがログに出てきたら、必ず正しくソースが書けているか等をチェックしましょう
配布されているサンプルソースは動きませんが、たまに役にたちます
http://www.impressjapan.jp/support/aftercare/download/rails-iphone-sample.zip
なお、このChapter辺りから、サンプルソースをコピペで作っています。
本の細かいミスは見落としている可能性があります。
逆に、本でわからない事があったら、サンプルソースを見たらわかる事もあるかもしれません。
『RailsとiPhoneではじめるアプリケーション開発』を進める時の注意点・ハマったこと Chapter 011
それでは気を改めて引き続き『RailsとiPhoneではじめるアプリケーション開発』を進めます。
ちなみに、さっき食べた夕食をなぜか吐いてしまいましたが、それはきっとこの本によるストレスのせいでは無いと信じたいですw

- 作者: 栗田由菜,丸山弘詩
- 出版社/メーカー: インプレスジャパン
- 発売日: 2013/08/21
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
『RailsとiPhoneではじめるアプリケーション開発』を進める時の注意点・ハマったこと Chapter 03まで - diceK66のブログ
Chapter 04 -> 06
『RailsとiPhoneではじめるアプリケーション開発』を進める時の注意点・ハマったこと Chapter 06まで - diceK66のブログ
Chapter 07
『RailsとiPhoneではじめるアプリケーション開発』を進める時の注意点・ハマったこと Chapter 07 - diceK66のブログ
Chapter 08 -> 09
『RailsとiPhoneではじめるアプリケーション開発』を進める時の注意点・ハマったこと Chapter 08 -> 09 - diceK66のブログ
:token_authenticatableが削除されたので他の方法で実装してみた
『RailsとiPhoneではじめるアプリケーション開発』を進める時の注意点・ハマったこと Chapter 010 - diceK66のブログ
Chapter 10
:token_authenticatableが削除されたので他の方法で実装してみた - diceK66のブログ
重要事項
以下の2つは必ず読みながら進めましょう
+ Objective-C - RailsとiPhoneではじめるアプリケーション開発をiOS7向けに読み進めるときの注意・修正点 - Qiita
+ 詳細 -- 栗田 由菜「RailsとiPhoneではじめるアプリケーション開発」Impress - みんなで正誤表
エラーが起きたらコンソールをじっくり読みましょう
特に自分が作成or編集したファイルがログに出てきたら、必ず正しくソースが書けているか等をチェックしましょう
配布されているサンプルソースは動きませんが、たまに役にたちます
http://www.impressjapan.jp/support/aftercare/download/rails-iphone-sample.zip
なお、このChapter辺りから、サンプルソースをコピペで作っています。
本の細かいミスは見落としている可能性があります。
逆に、本でわからない事があったら、サンプルソースを見たらわかる事もあるかもしれません。
p.259カオスのはじまり・・・いや既にカオスだったけど・・・
もはや本文で何を説明しようとしているのか意味が分かりません。
素直に配布されているサンプルソースをコピった方がよいでしょう。
ちなみに、TPTravelAddControllerというのは、TPTravelCreateControllerの間違いでしょう。
やっと「TP」が付いたのに惜しい!
で、やたらいろんなところに同じようなサーバーとの通信処理が書かれていますが、バグの温床になるので、どこかにまとめた方がよいでしょう。
そういうキレイなソースの書き方的なものはこの本に求めるのは止めた方が良さそうです。
BlocksKitは1.8.2を指定しましょう
おそらく、こんなエラーが発生すると思います。
No visible @interface for 'UIBarButtonItem' declares the selector 'initWithBarButtonSystemItem:handler:'
どうもBlocksKitのバージョンがあっていないのが原因のようです。
一度Xcodeを閉じ、Podfileを編集して、再度pod installすると解消します。
pod 'BlocksKit', '1.8.2'
auth_tokenをRailsに渡す
:token_authenticatableの代替策を実装したのでそれにあわせて、auth_tokenを渡す必要がありました(私の場合)。
「MR_」を付けましょう
MagicalRecordを使用しているので、contextにアクセスする時はMR_XXXじゃないのかな?
参考までに、auth_tokenとMR_の件を最低限修正したソースは以下の通りです。
(まだ問題が潜んでいるかもしれませんが・・・)
- (void)getData { TravelPhotoAPI *tp_api = [TravelPhotoAPI sharedInstance]; TPUserInfo *user_info = [TPUserInfo sharedInstance]; AFHTTPClient *sharedClient = [TravelPhotoAPI sharedClient]; NSString *url = [tp_api travelPath]; [sharedClient setParameterEncoding:AFFormURLParameterEncoding]; NSMutableURLRequest *request = [sharedClient requestWithMethod:@"GET" path: url parameters:@{@"user[email]" :[user_info loadEmail], @"user[auth_token]" : [user_info loadAuthToken]} ]; AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response,id JSON) { NSArray *travels = [JSON valueForKeyPath:@"travels"]; NSDictionary *user = [JSON valueForKeyPath:@"user"]; NSManagedObjectContext *context = [NSManagedObjectContext MR_defaultContext]; User *user_model = [User MR_findFirstByAttribute:@"id" withValue:[user valueForKey:@"id"]]; if(user_model == NULL){ user_model = [User MR_createEntity]; } user_model.id = [user valueForKey:@"id"]; user_model.username = [user valueForKey:@"username"]; [context MR_saveToPersistentStoreAndWait]; NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateFormat:@"yyyy-MM-dd"]; for (NSDictionary * travel in travels) { Travel *travel_model = [Travel MR_findFirstByAttribute:@"id" withValue:[travel valueForKey:@"id"]]; if(travel_model == NULL){ travel_model = [Travel MR_createEntity]; } // NSLog(@"%@",travel); travel_model.id = [travel valueForKey:@"id"]; travel_model.title = [travel valueForKey:@"title"]; travel_model.startdate = [formatter dateFromString:[travel valueForKey:@"startdate"]]; travel_model.enddate = [formatter dateFromString:[travel valueForKey:@"enddate"]]; travel_model.user = user_model; if ([[travel valueForKey:@"photo_url"] isEqual:[NSNull null]]){ travel_model.photo_url = @""; } else{ travel_model.photo_url = [travel valueForKey:@"photo_url"]; } // NSLog(@"%@",travel_model); [context MR_saveToPersistentStoreAndWait]; } _travels = [Travel MR_findAllSortedBy:@"id" ascending:NO]; [self.tableView reloadData]; } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) { NSLog(@"Error: %@", error); [SVProgressHUD showErrorWithStatus:@"エラーが発生しました"]; }]; [sharedClient enqueueHTTPRequestOperation:operation]; }
にしても、カオスなソースだなぁ。
p.278 clearUserInfoメソッドにも「MR_」が必要では?
こんな感じにしないとエラーになると思います。
- (void)clearUserInfo{ [self saveEmail:nil]; [self savePassword:nil]; [self saveAuthToken:nil]; NSManagedObjectContext *context = [NSManagedObjectContext MR_defaultContext];; [Travel MR_truncateAll]; [User MR_truncateAll]; [context MR_saveToPersistentStoreAndWait]; }
旅の登録処理がごっそり抜けてる!
ここで旅モデルに関する実装が終わったかのように見えますが、まだ肝心な旅の登録処理が実装されていません。
相変わらず、まさにこの時期入社してくる新入社員が書くようなカオスなソースですが、サンプルソースを修正して、最低限動くようにしたコードを記載します。
>|objc|
- (void)onSubmit{
[SVProgressHUD showWithStatus:@"送信中" maskType:SVProgressHUDMaskTypeBlack];
QEntryElement *title = (QEntryElement *)[self.root elementWithKey:@"title"];
QDateTimeInlineElement *startdate = (QDateTimeInlineElement *)[self.root elementWithKey:@"startdate"];
QDateTimeInlineElement *enddate = (QDateTimeInlineElement *)[self.root elementWithKey:@"enddate"];
if(startdate.dateValue == NULL || enddate.dateValue == NULL ||title.textValue == NULL){
[SVProgressHUD showErrorWithStatus:@"フォームに値が入力されていません。"];
return;
}
if([startdate.dateValue compare:enddate.dateValue] == NSOrderedDescending){
[SVProgressHUD showErrorWithStatus:@"日付が不正です"];
return;
}
//通信処理
TravelPhotoAPI *tp_api = [TravelPhotoAPI sharedInstance];
TPUserInfo *user_info = [TPUserInfo sharedInstance];
if([tp_api.networkStatus boolValue] == NO){
return;
}
AFHTTPClient *sharedClient = [TravelPhotoAPI sharedClient];
NSDictionary *userDic = @{@"travel[title]": title.textValue ,
@"travel[startdate]": startdate.dateValue,
@"travel[enddate]": enddate.dateValue,
@"user[email]" :[user_info loadEmail],
@"user[auth_token]" : [user_info loadAuthToken]
};
NSString *path = [tp_api travelCreatePath];
NSLog(@"%@",path);
[sharedClient setParameterEncoding:AFFormURLParameterEncoding];
AFJSONRequestOperation *operation = [AFJSONRequestOperation
JSONRequestOperationWithRequest:[sharedClient requestWithMethod:@"POST"
path: path
parameters: userDic
]
success:^(NSURLRequest *request, NSHTTPURLResponse *response,id JSON) {
[[NSNotificationCenter defaultCenter] postNotificationName:TPReloadTravels object:self userInfo:nil];
[self.navigationController popToRootViewControllerAnimated:TRUE];
[SVProgressHUD showSuccessWithStatus:@"作成が完了しました"];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(@"Error: %@", error);
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"エラーが発生しました" message:@"メールアドレスまたはパスワードが間違っています"
delegate:self cancelButtonTitle:@"確認" otherButtonTitles:nil];
[alertView show];
[SVProgressHUD dismiss];
}];
[sharedClient enqueueHTTPRequestOperation:operation];
}
|
You can only send 0 photo at a time?
p.287に『きちんとアップロードされているようです。』とありますので、一応このタイミングでテストしてみましたが、当然のごとくできません。
私の場合は、シミュレーターで写真を選択しようとすると、「You can only send 0 photo at a time」と言われ、写真を1枚も選択できませんでした。
これも正直原因がよくわかりませんでしたが、本と同じバージョンにあわせる事で解決しました。
//Podfile pod 'ELCImagePickerController', '0.1.1'
p.291TravelPhotoControllerがない
p.294で実装しますので、一旦エラーのまま進めます。
『PaperclipでS3に画像保存』はやりませんでした。
ただし、S3は個人的に使っているので、機会があれば何か参考になりそうな情報を提供したいと思います。
Chapter 11で残ってしまった問題
実は旅の写真をうまく表示する事ができず、困っています。
まず、最初の旅リストに表示されるリストのサムネイルは、RubyがRootからのURLしか返していないので、http://localhost:3000から返すように修正したところ、表示されるようになりました。

ところが、そこで旅を選んだ時の写真一覧が何故か表示されず、苦戦中です。

真っ白に見えますが、実はセル自体は生成されているので、タップすると拡大して表示する事ができます。
(カバー写真にも登録できます)

おそらくイージーミスだとは思うのですが、一旦あきらめて、最終章に進もうと思います。
所感
オブジェクト分け、メソッドの分割、プロジェクトの体系等、言い出したら切りがないほど改善すべき点はありますが、実際の現場ではこのようなカオスなプログラムと戦う事ってよくあります。
そういう意味ではとても「実践的」な本だとは思います。
ただ、一つだけ言えるのは、これだけトラブルと戦って、いろいろな事を調べながら進めていると、かなり実力が付いているのが実感できますw
:token_authenticatableが削除されたので他の方法で実装してみた
4月1日ですが、エイプリルフールエントリーではないので、ご安心を!
桜がきれいに咲いて、『RailsとiPhoneではじめるアプリケーション開発』で疲れた私の心を癒してくれましたw
今回は、こちらのエントリの補足です。
『RailsとiPhoneではじめるアプリケーション開発』を進める時の注意点・ハマったこと Chapter 010 - diceK66のブログ
タイトル通り、:token_authenticatableが新しいdeviseでは削除されているので、他の方法で実装してみました。
参考にしたサイト
http://gist.github.com/josevalim/fb706b1e933ef01e4fb6
http://stackoverflow.com/questions/18931952/devise-token-authenticatable-deprecated-what-is-the-alternative
Userテーブルにauthentication_tokenカラムを追加
$ rails g migration AddAuthenticationTokenToUser authentication_token:string
migrationファイルを編集
追加したauthentication_tokenカラムにユニークインデックスをはっておきます。
class AddAuthenticationTokenToUser < ActiveRecord::Migration def change add_column :users, :authentication_token, :string add_index :users, :authentication_token, unique: true #ここを追記 end end
DBに反映
いつものコマンドでDBに反映します。
$ rake db:migrate
application_controller.rbを修正
おそらく、現時点ではほぼ空になっているはずですが、以下のようにコードを追記します。
翻訳は適当ですのでご勘弁をw
class ApplicationController < ActionController::Base protect_from_forgery # これはDeviseのauthenticate_user!メソッドが呼ばれる前に呼び出される独自メソッドです。 before_filter :authenticate_user_from_token! # こちらがDeviseのメソッドです before_filter :authenticate_user! private def authenticate_user_from_token! user_email = params[:user][:email].presence user = user_email && User.find_by_email(user_email) # タイミング攻撃を防ぎつつ、paramsのトークンとDBのトークンを比較します if user && Devise.secure_compare(user.authentication_token, params[:user][:auth_token]) sign_in user, store: false end end end
userモデルにメソッドを追記
これだけだとカラムが追加されているだけなので、models/user.rb必要なメソッドを追加します。
(略) def reset_authentication_token! self.authentication_token = generate_authentication_token save end private def generate_authentication_token loop do token = Devise.friendly_token break token unless User.where(authentication_token: token).first end end end
以上で、正常にログインできるようになったと思います。
たまには桜でも見てリフレッシュするといい事ありますね!
その他の『RailsとiPhoneではじめるアプリケーション開発』の解説エントリーはこちら
Chapter 01 -> 03
『RailsとiPhoneではじめるアプリケーション開発』を進める時の注意点・ハマったこと Chapter 03まで - diceK66のブログ
Chapter 04 -> 06
『RailsとiPhoneではじめるアプリケーション開発』を進める時の注意点・ハマったこと Chapter 06まで - diceK66のブログ
Chapter 07
『RailsとiPhoneではじめるアプリケーション開発』を進める時の注意点・ハマったこと Chapter 07 - diceK66のブログ
Chapter 08 -> 09
『RailsとiPhoneではじめるアプリケーション開発』を進める時の注意点・ハマったこと Chapter 08 -> 09 - diceK66のブログ
Chapter 10
『RailsとiPhoneではじめるアプリケーション開発』を進める時の注意点・ハマったこと Chapter 010 - diceK66のブログ

- 作者: 栗田由菜,丸山弘詩
- 出版社/メーカー: インプレスジャパン
- 発売日: 2013/08/21
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る