Twitter認証を要求する際、謎の401エラーとなるときの原因のひとつ
サーバの時刻が実際の時間とズレている。
VirtualBoxで開発している場合、立ち上げっぱなしでクライアントマシンをスリープしていると、エミュレートしているOSもスリープしてしまう。
その場合、エミュレートしているOSの時刻を正しく修正する必要がある。
CentOSは以下で修正可能。
sudo service ntpd stop sudo ntpdate jp.pool.ntp.org sudo service ntpd start
たまにハマる。忘れないようにメモ。
railsでgenerateで生成したあとで取り消すコマンド
[hoge@localhost app]$ rails g controller plan create app/controllers/plan_controller.rb invoke erb create app/views/plan invoke test_unit create test/functional/plan_controller_test.rb invoke helper create app/helpers/plan_helper.rb invoke test_unit create test/unit/helpers/plan_helper_test.rb invoke assets invoke coffee create app/assets/javascripts/plan.js.coffee invoke scss create app/assets/stylesheets/plan.css.scss
planのコントローラを生成したが、ホントはplansだった。
その場合、以下のコマンドで取り消しが可能。
[hoge@localhost controllers]$ rails destroy controller plan remove app/controllers/plan_controller.rb invoke erb remove app/views/plan invoke test_unit remove test/functional/plan_controller_test.rb invoke helper remove app/helpers/plan_helper.rb invoke test_unit remove test/unit/helpers/plan_helper_test.rb invoke assets invoke coffee remove app/assets/javascripts/plan.js.coffee invoke scss remove app/assets/stylesheets/plan.css.scss
destroyコマンドで生成を取り消すことが可能。
railsでhas manyオブジェクトをViewで編集する方法
ネストされたオブジェクトの中身を画面表示する。
has_manyで紐付いているオブジェクトの中身を表示する方法がわからなかったのでメモ。
モデルの構成は以下のとおり。
class Event < ActiveRecord::Base # attr_accessible :title, :body attr_accessible :title, :memo, :image_attributes, :sub_events_attributes has_one :image, class_name: "EventPicture", dependent: :destroy has_many :sub_events , dependent: :destroy accepts_nested_attributes_for :image, allow_destroy: true accepts_nested_attributes_for :sub_events, allow_destroy: true end class SubEvent < ActiveRecord::Base # attr_accessible :title, :body attr_accessible :event, :time, :content, :index belongs_to :event end
eventオブジェクトの下にはhas_manyで複数のsub_eventが紐付いている。
これをViewで表示するためにはfields_forメソッドを使用する。
View側の処理は以下の通り。
<% @page_title = "イベントの編集" %> <h1><%= @page_title %></h1> <p><%= link_to "イベントに戻る" , @event %></p> <%= form_for @event, html: {multipart: true} do |form| %> <%= render "form", form: form %> <p><%= form.submit %></p> <% end %>
実際はformのヘルパで処理を実施。
<% @page_title = "新規・編集をするよ" %> <h1><%= form.label :title, "タイトル" %></h1> <h1><%= form.text_field :title %></h1> <!-- #mergeはハッシュの統合。optionが不要な場合は{}(からの配列)を設定させる。 --> <%= form.fields_for :image do |imgf| %> 画像のアップロード<br> <%= imgf.file_field :uploaded_image %> <% if !imgf.object.new_record? && imgf.object.errors.empty? %> <%= event_image_tag @event, width: 600 %> <%= imgf.check_box :_destroy %> <%= imgf.label "削除する" %> <%= imgf.hidden_field :id %> <% end %> <% end %> <table class= "subevent"> <tr> <th><%= form.label :time, "所要時間" %></th> <th>内容</th> </tr> <%= form.fields_for :sub_events, form.object.sub_events do |sevf| %> <% if sevf.object.errors.empty? %> <tr> <td><%= sevf.text_field :time %></td> <td><%= sevf.text_field :content %></td> <%= sevf.hidden_field :index %> </tr> <% end %> <% end %> </table> <%= form.label :memo, "メモ" %> <%= form.text_field :memo %>
<%= form.fields_for :sub_events, form.object.sub_events do |sevf| %>
この部分でevent配下のsubeventオブジェクトを1つずつ取得しsevfへ代入している。
(表示するカラムと受け渡すオブジェクトを指定)
その後はオブジェクトが1つの場合と同様に処理を記載していく。
App Extensionでアプリ間連携をした際、host appの挙動調査
こちらのサンプルを使用させていただきました。
[iOS 8] App Extension #4 – Action Extension | Developers.IO
※今後追加していく予定。
・host側のアプリはバックグラウンドには入らずcontain appを呼び出す。
・contain appでは通信可能?
→webViewは動作している。EvernoteやPocketでも通信しており、独自サーバへのアクセスも出来そう。
・contain appからの戻り値はhost側にブロックを使って定義。Arrayにobjectを入れて返却.
Ruby(Rails)にAPI作成支援ライブラリGrapeで任意の返却値を返す方法
サンプルを見ているとRailsのモデルをそのまま返すというサンプルが多く、モデルのなかでも必要な項目(idやタイムスタンプを除いて)を返すというサンプルがなかったのでメモ。
module TestApp class API < Grape::API prefix 'api' version 'v1', using: :path format :json resource :tweeturls do get do arrayUrl = Array.new(100) objs = Tweeturl.first(100) objs.each do |obj| arrayUrl.push({"url" => obj.url}) end hashobj = Hash["urls" => arrayUrl] present hashobj end end end end
モデルは以下
class CreateTweeturls < ActiveRecord::Migration def change create_table :tweeturls do |t| t.string :url t.timestamps end end end
TwitterUrlsというモデルにはタイムスタンプとurl、自動で付与されるidを持っている。
そのモデルからurlだけを100個返却するのがAPIの内容。
自分でArrayやHashで組み立てて、presentで返却すれば作成完了。
iPhoneアプリでOAuth2.0認証からInstagramのAPIを叩いて情報を取得するまで。
InstagramなのでOAuthは2.0。
Webへのアクセスは有名ドコロのライブラリを使用。
使用するライブラリ
OAuth2 : OAuth2Client
HTTP通信 : AFNetworking
画像の非同期ダウンロード : SDWebImage
各ライブラリの詳細については以下の参考情報を参照。
参考情報
OAuth2
Selection 9: iOSでOAuth2認証を行う(feedlyクライアントの作成)
HTTP通信、画像の非同期ダウンロード
DeNAのiOSエンジニア内で利用頻度の高いライブラリをランキング化してみました #iOS #DeNA|CodeIQ MAGAZINE
InstagramのAPI
Instagram APIの使い方まとめ(サンプルコード付)
プログラム起動前に実施すること
クライアントIDとクライアントシークレットを設定すること
AppDelegate.m //clientId static NSString * const kOauth2ClientClientId = @"xxx"; //クライアントIDを設定 //Client Secret static NSString * const kOauth2ClientClientSecret = @"xxx"; //クライアントシークレットを設定
少しつまづいた所
OAuth2Client
クライアントIDやクライアントシークレットをセットするメソッドが参考情報と異なります。
keyChainGroupというトークンの情報をキーチェーンに保存するときに一意に識別するためのキー情報を設定する必要があります。ここの例では"hoge"としています。
AppDelegate.m + (void)initialize { NSString *authUrl = [kOauth2ClientBaseUrl stringByAppendingString:kOauth2ClientAuthUrl]; NSString *tokenUrl = [kOauth2ClientBaseUrl stringByAppendingString:kOauth2ClientTokenUrl]; [[NXOAuth2AccountStore sharedStore] setClientID:kOauth2ClientClientId secret:kOauth2ClientClientSecret scope:[NSSet setWithObjects:kOauth2ClientScopeUrl, nil] authorizationURL:[NSURL URLWithString:authUrl] tokenURL:[NSURL URLWithString:tokenUrl] redirectURL:[NSURL URLWithString:kOauth2ClientRedirectUrl] keyChainGroup:@"hoge" forAccountType:kOauth2ClientAccountType]; }
AFNetworking
GETで情報を取得する際のパラメータへの設定方法。
NSDictionaryでパラメータ名をキーに、値をキーに対する情報として定義
作成した情報をアクセスメソッドのparametersにセットする
ViewController.m - (void)p_getUserProfile:(NXOAuth2Account *)account { //get user profile on feedly user NSLog(@"account info : %@", account); NSLog(@"accessToken=%@",account.accessToken.accessToken); // AFNetworkingライブラリ使用箇所 // AFHTTPRequestOperationManagerを利用して、InstagramからJSONデータを取得する AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; //get profile NSDictionary *params = @{@"access_token":account.accessToken.accessToken}; [manager GET:@"https://api.instagram.com/v1/users/27069716/" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) { // 通信に成功した場合の処理 NSLog(@"responseObject: %@", responseObject); NSString *url = responseObject[@"data"][@"profile_picture"]; NSURL *imageURL = [NSURL URLWithString:url]; // SDWebImage使用箇所 UIImage *placeholderImage = [UIImage imageNamed:nil]; [_imageView setImageWithURL:imageURL placeholderImage:placeholderImage]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { // エラーの場合はエラーの内容をコンソールに出力する NSLog(@"Error: %@", error); }]; }
初期画面でSign Inボタン押下でアクセストークン取得。
Get InformationでInstagramのAPIを叩いて私のInstagramの情報を取得。
取得した情報からプロフィールの写真のアドレスを抜き出し、非同期で画像を取得。
エラーハンドリングは考慮できてないです。
作成したプログラムはgithubに置きました。
https://github.com/jtaka1012/OAuthTest
UIScrollViewで上に余分な隙間が出来てしまう時の回避方法
追記
こちらに解決方法を書きました。
UITableViewの上にヘッダー分の隙間が開くときの対処法 - Simple gadget life programming diary
SCrollViewのエリアをグレーに、貼り付けるViewを赤にして再現。
ちなみにグレーのエリアと赤いViewは同じ高さ。下のキャプチャでは赤いViewが埋まったようになっている。
調べるとどうやら64pix分余分な隙間がある。
そのため以下のようにして回避。
UIScrollView *_scrollView = [UIScrollView new]; _scrollView.frame = CGRectMake(0, 164, 320, 320); _scrollView.pagingEnabled = NO; _scrollView.showsVerticalScrollIndicator = NO; _scrollView.showsHorizontalScrollIndicator = YES; _scrollView.scrollsToTop = NO; _scrollView.backgroundColor = [UIColor grayColor]; _scrollView.delegate = self; //赤いView。y座標開始位置を-64に。 UIView *kariview = [[UIView alloc]initWithFrame:CGRectMake(0, -64, 1320, 320)]; kariview.backgroundColor = [UIColor redColor]; [_scrollView addSubview:kariview]; //ScrollViewのcontentSizeの高さも-64に。 _scrollView.contentSize = CGSizeMake(kariview.bounds.size.width, kariview.bounds.size.height-64); _scrollView.bounces = YES; [self.view addSubview:_scrollView];
これで実行すると、
ちゃんと表示される。上下にも動かない。
本当はもっと正しい設定方法があるような気がする。。