ラベル XCode の投稿を表示しています。 すべての投稿を表示
ラベル XCode の投稿を表示しています。 すべての投稿を表示

2017年2月14日火曜日

CocoaPods で Quick のインストールが上手く出来ない

テスト書いてますか?
久々に Xcode を使ってみようと思って、その際にテスティングフレームワークの選択で Quick を使おうと思いました。

Quick(github)
https://github.com/Quick/Quick

さて、インストールと思って、どうやって導入するのか確認したところ、ローカライズされたページが用意されていました。

https://github.com/Quick/Quick/blob/master/Documentation/ja/InstallingQuick.md

インストールの手段としては、Git Submodules を使う方法、CocoaPods を使う方法、Carthage を使う方法と 3 パターン用意されていました。

ここでは、使い慣れている(?) CocoaPods を使ってインストールしようと思ったので、該当ページを確認しながら導入をしてみました。

ちなみに、環境は
  • Xcode 8.2.1
  • Swift 3.0.2
  • CocoaPods 1.2.0
です。

早速、書いてある通り、Podfile を作成しその中に設定を記述します。
# Podfile

use_frameworks!

def testing_pods
    pod 'Quick', '~> 0.9.0'
    pod 'Nimble', '3.0.0'
end

target 'MyTests' do
    testing_pods
end

target 'MyUITests' do
    testing_pods
end

保存して終了後、インストールコマンドを走らせ無事インストールが完了し、単純なテストを書いてみようとした所、import Quick の部分で「No such modeule ‘Quick’」のエラーが発生しました。

こういった部分が面倒だなと思い、import 文を外してビルドすると、なんとそこでもエラーが。。。

インストールに失敗したのかと思い、再度ページを見ながら慎重に行っても状況変わらず。
ビルドエラーの内容を見ても、Quick の部分でエラーになっていることは分かるのですが、ライブラリの中を変更すると CocoaPods の良い所がなくなるので、その他の方法を当たるようにしました。

探してみた所、どうも Podfile の記述方法を変えれば良いことがわかりました。
で、変更した内容が以下の通り
# Podfile

use_frameworks!

def testing_pods
  # pod 'Quick', '~> 0.8.0'
  # pod 'Nimble', '3.0.0'
  pod 'Quick', :git => 'https://github.com/Quick/Quick.git'
  pod 'Nimble', :git => 'https://github.com/Quick/Nimble.git'
end

target 'QuickSampleTests' do
  inherit! :search_paths
  testing_pods
end

target 'QuickSampleUITests' do
  inherit! :search_paths
  testing_pods
end

post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['SWIFT_VERSION'] = '3.0' # or '3.0'
    end
  end
end
※ QuickSample というプロジェクト名にしています。

どうやら、pod ファイルを github から取得する必要があり、更に build_settings の部分に [‘SWIFT_VERSION’] = ‘3.0’ をセットするようにしてインストールすれば、import の部分でもエラーにならず、またテストも無事動くようになりました。

なんだろう?inherit の部分や、post_install のイテレータ部分とかは、公式のインストール方法には書かれていない内容だったので、若干戸惑いましたが、なんとか出来て良かったです。
Written with StackEdit.

2015年12月29日火曜日

NavigationBar が View に重なる対応について

今更ながらのところでもあるけど、UINavigationController を使用している場合に、storyboard 上で、NavigationBar が View に重なる対応についてどうすれば良いかを考えて見ることにする。

この内容は iOS7 から View がフル画面表示に変わった時から発生した内容なので、なぜ今頃という面があるかもしれないが、今一歩良い対応方法が見つからないので、調べてみることにした。

ちなみに、今までは、先にビューのデザインをして、オブジェクトを配置後に、座標0, 0 の位置にずらす方法で対応していた。
よって、最終的にはナビゲーションバーの下にオブジェクトが配置される様になる。
この方法で、実行時には問題なく表示されるのだが、問題点はメンテナンスを行う際に、ナビゲーションバーの下のオブジェクトを見逃す場合があると言うこと。
(そんなバカなと思うかもしれないが、焦って対応をする場合などは案外見落としがちになる)
その他、あえてナビゲーションバー分下にビューをずらす方法も考えたが、Autolayout の制約が難しくなるので却下した。

この問題に対応すべく storyboard を使用した場合の良い方法を調べてみることにした。
調べてみると、みんなどうやってこの部分を対応しているかよく分からない。
この内容でよく引用されているのが以下の記事。
やはりお前らのiOS7対応は間違っている(解説編)
http://qiita.com/yimajo/items/254c7cebab7864678246
上記や、その他のサイトを見る限り以下の方法がある。
  1. ViewController の Extend Edges の Under Top Bars と、Under Bottom Bars のチェックを外す
  2. 上としっしょだが、コードで指定する
    self.edgesForExtendedLayout = UIRectEdgeNone;
  3. NavigationController の Navigation Bar の Translucent のチェックを外す
いずれも、ビュー開始位置がナビゲーションバーの下からになる。
(すりガラス的な効果が消えたふうに見える)
その他、各 ViewController の Extend Edges の Under Top Bars のチェック、
同じく、ViewController の Adjust Scroll View Insets のチェックを外すなどもある。

うーん。
調べてみた結果自分の困っている内容(Xcode での視界性の悪さ)とは別の内容(アプリの仕様というか不具合対応)での回答が多いようだ。

ここいら辺を踏まえて、自分の問題に対するベターと思われる方法を考えてみた。
方法は、上記のいずれのチェックも外さずに、NavigationController の Attributes にある Shows Navigation Bar のチェックを外し、コード上でこの属性を有効にする方法。
これであれば、Storyboard からは、ナビゲーションバーの表示が消えて、作業・確認しやすくなる。
制約等も直感的に付けることが出来る。
この場合は、ナビゲーションバー先頭の ViewController の viewDidLoad で
[[self navigationController] setNavigationBarHidden:NO animated:YES];
// Swift
self.navigationController?.setNavigationBarHidden(false, animated: true)
とするか、サブクラスを作成して、上記のコードで指定するなどが必要。
難点は、画面上からナビゲーションバーが消えてしまうので、ナビゲーションバーが存在しないかと思ってしまう点か。
(storyborad に UINavigationController が出ているから大丈夫とは思うが)
あくまで、ベターなのでベストがわからない。。。

しかし、なぜこんなに苦労が必要なのだろうか?
storyboard で見えたとおりに表示されないという Xcode 側に問題がある気がするのだが(ナビゲーションバーとフル画面を表示したいなら storyboard でナビゲーションバー部分を別表現すればいいのに)、Xcode 作っている開発者は違和感覚えないのだろうか?
Written with StackEdit.

2015年12月5日土曜日

Xcode 7.1.1 で iOS7 のシミュレータを動かす方法

覚書として Xcode7 から、シミュレータの対応する iOS のバージョンが 8.4 以降になったようだ。
実際、iPhone4s iOS7 の組み合わせでシミュレータを実行しようとすると、
The iOS 7.1 simulator runtime is not available. Unable to open liblaunch_sim.dylib. Try reinstalling Xcode or the simulator runtime.
のエラーがでて実行できない。
Apple 自体が古い OS のサポートは切っているので、古い OS に対応したアプリなんか作るなと言うことだろうか。
確かに、古い端末をずっと使われたら商売上がったりだもんね。

とはいえ、世の中そう甘くない。
慣れ親しんだ端末をアップデートせずに使う人もいるわけで、最近では買い替え後の端末を有効利用してやろうと考える人もいる。
だから Apple よもう少し寛容になってくれ。。。
(MS なんて VB6 のサポートまだやってるんだぞ)

さて、本題だが、この状態が発生したが、自分では実機を持っているので最悪シミュレータを使用しなくても良い環境にある。
なので、調査はしたけど試してないので、どうしても必要になったら実行に移そうと思う。
参考にしたのは以下のサイト

Xcode 7 付属のシミュレータに iOS 7.1 シミュレータを登録する
http://qiita.com/msa_uccky/items/f6ed080e5d723d4c0793

How can I run the iOS 7.1 Simulator in Xcode 7.0 beta 2?
http://stackoverflow.com/questions/31056634/how-can-i-run-the-ios-7-1-simulator-in-xcode-7-0-beta-

安定の Qiita と Stackoverflow 凄いサイトだ。
この記事を読むと、エラー文にも有った liblaunch_sim.dylib が古い OS に対応していないので、Xcode7 アプリ内にある iOS7 のファイルを使用するように変更する手順が書かれている。

というかみんなどうやってこういう情報を得るのだろうか。探究心が足らないのだろうか?

最終的には以下の様にすれば良いらしい

1. 現在 /Library/Developer... にある liblaunch_sim.dylib をバックアップ
$ sudo mv "/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 7.1.simruntime/Contents/Resources/RuntimeRoot/usr/lib/system/host/liblaunch_sim.dylib"{,.bak}

2. Xcode アプリ内にある liblaunch_sim.dylib を上記の /Library/Developer... にシンボリックリンクを張る

$ sudo ln -sf "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/lib/system/host/liblaunch_sim.dylib" "/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 7.1.simruntime/Contents/Resources/RuntimeRoot/usr/lib/system/host/liblaunch_sim.dylib"
これで、iOS7 のシミュレータが動くはず。

2015年5月28日木曜日

続、Facebook SDK for iOS 4.0 に悩まされる

先日、表題の記事を書いた所、コメントを頂きましたのでそちらの確認をさせて頂くのと、Graph API を使用する方法を試してみましたので報告を。。。

まず、コメントで頂いた方法を試させていただきました。
(kota n さん。ありがとうございます。)
さっそく必殺のコピペをして動作を見てみるとログに以下のメッセージが。。。
FBSDKLog: Warning: [FBSDKAccessToken currentAccessToken] is missing publish_actions permissions
どうやら publish_actions という権限が必要なようです。
この権現の扱いというのがややこしく、理解するのに暫く時間がかかりました。

しかも、この publish_actions の権限は、Facebook に審査を通していないと、使用時に警告文が表示されるようです。
(あくまで、警告文なので製品アプリだとまずいですが、お試しなので強行突破すれば確認を取ることは出来ます。 テストユーザ(警告が出ない?)ってものあるようですが調査不足。)

この権限の付与は、Graph API を使用する際にも設定の必要がありますので、ひとまず Graph API を使用した方法を載せます。

権限を付与するためには、まずログインの動作が必要となります。
ログインは以下コードでログイン動作を行います。
    - (void)login
    {
        if ([FBSDKAccessToken currentAccessToken]) {
            // accessToken が取れるならログイン処理は完了している
            reutrn;
        }

        FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
        [login logInWithReadPermissions:@[@"email"] handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
            if (error) {
                // Process error
                NSLog(@"Error");
            } else if (result.isCancelled) {
                // Handle cancellations
                NSLog(@"Cancel");
            } else {
                // If you ask for multiple permissions at once, you
                // should check if specific permissions missing
                if ([result.grantedPermissions containsObject:@"email"]) {
                    // Do work
                    NSLog(@"Login OK");
                }
            }
        }];

    }
このコードを走らせると、Safari が起動してログイン画面が起動し、その後アプリのアクセス許可を聞いています。
OK または、キャンセルを押すと、アプリに戻ります。

OK を押した場合はログインと、アプリの使用許可が出来た形となります。
次に、publish_actions の権限を付与するコードを使用します。
ログインが出来た状態だと、権限の付与ができるようになっています。
    - (void)addPermission
    {
        if ([FBSDKAccessToken currentAccessToken]) {
            // publish_actions の権限確認・付与
            if ([[FBSDKAccessToken currentAccessToken] hasGranted:@"publish_actions"]) {
                // TODO: publish content.
            } else {
                FBSDKLoginManager *loginManager = [[FBSDKLoginManager alloc] init];
                [loginManager logInWithPublishPermissions:@[@"publish_actions"] handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
                    //TODO: process error or result.
                }];
            }
        }
    }
またここでも、権限の付与確認のために Safari が起動します。
ここで、審査を通していないと、赤文字で警告文が表示される様になっています。
日和らずテストなので OK しましょう。

ここまで来ると、あとはメッセージを投げるだけ!以下の内容の処理を使用します。
    - (void)sendMessage:(NSString *)message
    {
        if ([[FBSDKAccessToken currentAccessToken] hasGranted:@"publish_actions"]) {
            [[[FBSDKGraphRequest alloc]
              initWithGraphPath:@"me/feed"
              parameters: @{ @"message" : @"hello world"}
              HTTPMethod:@"POST"]
             startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
                 if (!error) {
                     NSLog(@"Post id:%@", result[@"id"]);
                 }
             }];
        }
    }
これで、完了と。。。
非常に端折って書いているので、正確性は怪しいですが投稿はできるはず。。。

コメントで頂いた内容についても、public_actions の権限を設定したあとからだと、うまくメッセージが送ることを確認しました。
ただ、同じメッセージが2回投稿される現象がでている(!?)ので調査が必要な状態。

気になる点は、権限を外したりログアウトはどうやるのかですが調べた所、権限の削除については、コードから可能。
ログアウトの処理は、Safari で Facebook を開くとログイン状態になっているので、そこからログアウトすれば良いみたい(これもコードで出来るのか?)。
登録したアプリ自体も Facebook の自分のページの設定の項目から削除することができます。

なんか投稿までの流れはコードでできたけど、解除や削除は手動になるっていうのが、今ひとつな感じです。
ともあれ、Graph API を使用しての投稿ができたので、ここから掘り下げていけば良いかなと思う次第です。

駆け足で書いたので、間違っているかもしれません。
しかし、なぜメッセージ等の投稿が Graph なんだろうか?

2015年2月14日土曜日

フェードアウトした(している)ボタンがクリック出来ない

iPhone アプリで、ボタンに画像を指定した物を配置し、フェードイン・フェードアウトのアニメーションをするものを作成してみた。

実装をして、確認を取るが、自分の悪い癖で、表示の確認をした時点で満足し出来たものとしていた。
が、しかし、あることを思いつく。

「これ、消えた時にクリックできるのかしら?」

こんな疑問点が出てきたが、なんとなく予想が付いたので、腫れ物を触るかの気持ちでタップした所、案の定タップイベントが動かない。。。

どうやら、hidden プロパティを YES、または、alpha を 0 にすると、タップは無効になるようだ。
また、フェードアウトの時は、消えかかっている状態でもタップは無効となる。

地味に、alpha を 0.05 にすると、タップは出来るが、正に亡霊の如く消えずに残っているので見た目が悪い。

ボタンのラベル文字をアニメーションさせるのであれば、最悪ボタン自体ではなく、中のラベルの部分をフェードイン・アウトさせてやれば、ラベルが消えてもタップは可能となる。
しかし、今回は画像で、ボタンの位置をコードで変更している様にしていたのだが、画像をフェードイン・アウトさせると何故かボタン位置が変更前に戻るアニメ付きでフェードイン・アウトするという動きとなった。

数パターンあたってみたが、ボタン単発で表示が消えた時にタップの動作を取得する事は出来なかった。

結論から言うと、今回取った手段としては、フェードイン・アウトさせるオブジェクトの上に透明のボタンを配置して、処理を分離させる様にし実現を行った。

コードは以下のとおり

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    [self fadeOut];
}


- (IBAction)buttonPush:(id)sender
{
    NSLog(@"Push The Button");
}

- (void)fadeOut
{
    [UIView beginAnimations:@"hideOverlayButtonFadeOut" context:nil];
    [UIView setAnimationDuration:1.5];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(fadeIn)];
    [self._button setAlpha:0.0];
    [UIView commitAnimations];
}

- (void)fadeIn
{
    [UIView beginAnimations:@"hideOverlayButtonFadeIn" context:nil];
    [UIView setAnimationDuration:1.5];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(fadeOut)];
    [self._button setAlpha:1.0];
    [UIView commitAnimations];
} 

ボタンを2つ用意して、ひとつはフェードイン・アウトを行うためのボタン(別段ボタンでなくても良い)を用意して、その上に実際にイベントを取る透明のボタンを配置する。
一応出来たが、一つのボタンの為にもう一つボタンを使用するのが、なんだか面倒。

何かいい方法あるんじゃないだろうか。。。

Written with StackEdit.

2014年1月14日火曜日

Apple からの「Build your apps for iOS 7.」のメール

昨年から情報は出ていたが、今朝改めてタイトルのメールが来た。

Make sure your apps work seamlessly with the innovative technologies in iOS 7. 
Starting February 1, new apps and app updates submitted to the App Store must be built with Xcode 5 and iOS 7 SDK.
Learn more about building apps for iOS 7.

訳としては、

あなたのアプリが iOS7 に対応している事を確認指定ください。
2 月 1 日から、新しいアプリの登録や更新時には、Xcode 5 と iOS7 でビルドしたものが必要となります。
詳しくは、iOS7 でのアプリ作成方法を参考にしてください。

と、いうことらしい。

最近別件にて、iOS アプリに手を付けていないので、こういうの見るとスタックが積まれた感じが半端ない。。。
ただ、こういった事により、古い OS のサポートをしなくても良くなるというのは、これはこれで良かったりもする。

とにかく、次回の登録からは Xcode5 と iOS7 の組み合わせでの登録ということでやっていこう!

2013年10月23日水曜日

iOS シミュレータで実行時に「App をインストールできませんでした」と出る

Xcode5 に上げて、4 の頃のアプリを起動しようとすると

「App をインストールできませんでした」

と出て、実行できない。
こういうとき、アップグレードして失敗したなと思うが、
いつまでも古いバージョン使ってられないからね。

解決方法を調べてみた結果、数パターンあるようで、

その1
起動しているシミュレータがあれば停止させる

アプリのバイナリを削除
/Users/xxxx/Library/Application Support/iPhone Simulator/6.1/Applications/
の中のファイルを削除(個々の文字列のフォルダへ移動してターゲットのアプリ名がある物を削除)

Xcode でクリーン処理(Product→Clean)

実行

その2
iOS シミュレータのメニューで、
「iOSシミュレータ→コンテンツと設定をリセット…」 を実行

Xcode でクリーン処理(Product→Clean)

実行

その3
シミュレータ上のターゲットのアプリを削除(アイコン長押し→削除)

一応シミュレータ再起動

Xcode でクリーン処理(Product→Clean)

実行

いずれにせよ古いバイナリが絡んでいる模様。
またどのパターンも対処後、 Xcode でのクリーン → ビルドはした方がよさそう。

今ひとつ原理は分からないが、タイムスタンプとか比較して判定してるとかなのか?
ひとまず、対処法は分かったので、以降ビビル事はないな。

2013年10月5日土曜日

iOS シミュレータの OS バージョンを 6 系で動かす

Xcode5 が iOS7 発表と同じ時期に正式リリースとなっていた。

自分自身の iPhone を iOS7 に上げずに使っていたので、アップデートを保留しておいたが、先日良いタイミングで Xcode のバージョンアップをしてみた。

してみたは良い物の、Xcode5 もフラットデザインを採用しているので、まずびっくり。
そして、標準の iOS のバージョンが 7 になっていた。

当然 iOS シミュレータについても、起動すると iOS7 の画面が表示される。
ここまでは良かった。

古い Xcode4 系 + iOS6 のプログラムを動かしてみたところ、、、

「!!?コントロールに位置がずれている。。。だと」

そう、たぶんどこか新しくなって座標がずれているっぽい。

ああ、やっちまった。。。

と、実機へそのままアプリを送り込むと、表示ずれていない。
う~んなんとも。。。

とりあえず、iOS6 の頃に戻せるかと調べてみたところ、シミュレータの見た目等は無理だが、iOS のバージョンが指定できる事が分かった。

あと、Storyboard についても古い Xcode のバージョンを開くと新しいバージョンに変更するか聞いてきて、そのままアップグレードして、これまた少し悩んだので、これの解決(?)方法も書いておく。

まず、シミュレータのバージョンについては、
メニューから Xcode ->  Preference と開いて Downloads の画面を開くと、
インストール可能な旧 iOS  が出てくるので、欲しい物をインストールする。
あとは、Scheme バー(名前が分からんけど)の実行する対象を選択する箇所で iOS のバージョン選択が可能となっている。
これで、iOS の選択は完了。
あとはビルド環境などが iOS7 になっていないかを確認すればよい(プロジェクトの設定画面で選べる)。

次に Storyboard については、Storyboard を開いて、キャンパス(背景部分)をクリックして、File Inspector の Interface Builder Document の Opens in を Xcode 4.6 にすれば元に戻す事ができる。

切り替え時期なので、ある程度普及するまではこうやって確認していこうかと思う。

2013年9月30日月曜日

CoreData での DISTINCT 取得

CoreData で重複の無いデータを取得する方法が必要だったので調べてみた。
NSFetchRequest の setReturnsDistinctResults に YES を渡せばよい。

具体的なコードは、以下の通り
今回は例として、name が重複しているデータから重複を取り除いてデータを取得する処理を行う。
    // NSFetchRequest は、検索条件などを保持するオブジェクト
    // 後続処理で、このインスタンスに色々と検索条件を設定する。
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    
    // 検索対象エンティティを指定する。
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Theme" 
                                              inManagedObjectContext:_managedObjectContext];
    [fetchRequest setEntity:entity];
        
    // ソートを設定
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
    NSArray *sortDescriptors = @[sortDescriptor];
    [fetchRequest setSortDescriptors:sortDescriptors];

    // コードで絞り込んだ値を取得する
    NSDictionary *entityProperties = [entity propertiesByName];
    [fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:[entityProperties objectForKey:@"name"]]];
    [fetchRequest setReturnsDistinctResults:YES];   // ← ここで指定する
    
    [fetchRequest setResultType:NSDictionaryResultType];
    
    NSError *error;
    NSArray *fetchResults = [_managedObjectContext executeFetchRequest:fetchRequest error:&error];
    if (fetchResults == nil) {
        // Handle the error.
        NSLog(@"fetch error.");
    }
これで、NSArray に重複が除去されたデータが格納されている。

UITabBar のアイテムを選択する

KIF を使用してテストを書こうとしたが、TabBar のアイテム選択の方法が分からない。

UITabBarController を使用してボタンを選択する方法は良くあるけど、UITabBar だけでの選択方法の情報が無かった。

とはいえ、やはり実現している方はおられたので参考にさせていただいた。

【文法】UITabBarのItem(UITabBarItem)の切り替え
http://teapipin.blog10.fc2.com/blog-entry-66.html

現在サンプルで作成しているアプリがナビゲーションコントロールも使っている事もあって、TabBar コントロールを取得するまでが、これまたしんどかったが、こちらも情報が有ったので知識を拝借させて頂いた。

UIViewからUIViewControllerを見る
http://ameblo.jp/pyon-prog/entry-11329857939.html

上記 2 つを合わせて KIF 用のテストを以下のように書いた。
    -(void) testTabBar0Push {
                
        // TabBar の Delegate を発火させるため ViewController まで辿る
        UIApplication *app = [UIApplication sharedApplication];
        UIWindow *window = app.keyWindow;
        
        // rootViewController を取得
        UIViewController *uiview = (UIViewController *)window.rootViewController;
        
        // NavigationController から対象の ViewController を取得
        UINavigationController *navigationController = [uiview.childViewControllers lastObject];
        NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:navigationController.viewControllers];
        ViewController *view = (ViewController *)[viewControllers objectAtIndex:0];
     
        // TabBar
        UITabBar *tabbar = (UITabBar *)[tester waitForViewWithAccessibilityLabel:@"tabBar"];
        UITabBarItem *item = [tabbar.items objectAtIndex:0];
        
        // 選択
        [tabbar setSelectedItem:item];
        [view tabBar:tabbar didSelectItem:item];
        
        // 確認用コードを以下に書く
        ...
    }
どう考えても力業過ぎてコレで良いのか?
と思うがひとまずの動きが出来ているので良しとしようと思う。

今後もっとスマートな方法があれば、また別の記事で書こうと思う。

2013年9月27日金曜日

applicationDidBecomeActive が呼ばれた時に ViewController のメソッドを呼ぶ

CoreLocation を使うアプリを作っているが、アプリを閉じる→再開の繰り返しをすると、数回に一度アプリが落ちる現象が出る。

Xcode のデバッガには、「com.apple.CoreLocation.ConnectionClient」と出て落ちているから当たりは間違い無いとは思うが、原因が分からない。

処理としては、CoreLocation の開始を viewDidLoad で行ってた。
そして、バックグラウンドからの復帰時に CoreLocation の開始が行われていないため、無理矢理 applicationDidBecomeActive で、viewController の viewDidLoad を呼ぶようにしていた。
つまり、連続での CoreLocation のスタートをかけているのがまずいのでは?と思い、この部分にあたりを付けてみた。

さて、この場合 1 回だけ Location をスタートするにはどうすれば?

NSLog を張り巡らして見ていると、真っ新からの起動であろうと、バックグラウンドからの起動であろうと、applicationDidBecomeActive を通る事が分かった(公式のドキュメントにも書いてある)。

要するに、applicationDidBecomeActive で、Location の開始処理を行えば良いのだが、applicationDidBecomeActive は、AppDelegate でのメソッドのため処理の関係上 Location のスタートを開始するには不適切だった。
そこで、applicationDidBecomeActive に入ったときに ViewController のメソッドが発行でき、そのメソッドで Location の開始が出来れば良いと思い処理をする方法を調べたところ、以下の実現方法があったので採用した。

これは通知処理を使って、applicationDidBecomeActive が発生したときに発火するメソッドを登録すると言う物。
この登録を viewDidLoad で行う。
当然、いままで有った Location スタートは削除する。
    -(void) viewDidLoad {
        ...
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(applicationDidBecomeActive)
                                                     name:UIApplicationDidBecomeActiveNotification
                                                   object:nil];
        ...
    }
また、applicationDidBecomeActive 後に発動するメソッドを記述する。
ここに、Location のスタートをする処理を入れる。
    // applicationDidBecomeActive が呼ばれた時に実行する
    - (void)applicationDidBecomeActive {
        // CoreLocation 開始
        [_manager startUpdatingLocation];
    }

これで、立ち上げ→バックグラウンドの連続操作を繰り返してみたところ、アプリが落ちる事が無くなった(とはいえ、安心は出来ないので後日合間を見て検証をしてみる)。
他の遷移の場合を考えたらもう少し手間をかける必要は有ったが、取りかかりとしてはこの線で良かった。

2013年9月25日水曜日

KIF を使ってみる

iOS用の結合テストフレームワーク KIF を使ってみた。
ちなみに KIF は、「Keep It Functional」と言う事らしい。

KIF
https://github.com/kif-framework/KIF

Web で情報を集めてインストール等を行ってみたが、どうやらバージョンが違うらしく、
その通りにやってもエラーが出たので、半ば折れかけになってしまった。
公式の英語を頑張って見れば何とかなったので残しておく。

ちなみに今回インストールするバージョンは、 2.0 系
なお、ここでは、テスト被対象を、「製品ターゲット」とし、テストユニットのプロジェクトを「テストターゲット」として記述していく。

1. 製品プロジェクト(製品ターゲット)の作成
今回も適当に Single View Application のプロジェクトを作成する。

2. テストターゲットの作成
プロジェクトナビゲータを開き、下部にある「Add Target」ボタンを押す。
ターゲットの選択が出るので、iOS -> Other を選択し、
「Cocoa Touch Unit Testing Bundle」 を選択する。

プロジェクト名は、任意。
今回は、「Integration Tests」とした。

これで作成した名前と同じフォルダがナビゲータに作成されている。
このフォルダ内にあるファイル(.m/.h)は不要なため削除する。

ここで、一旦 Xcode を閉じる。

3. KIF のインストール
KIF のインストールは、前回使い方を学んだ CocoaPods を使用する。
ターミナルを開き、上記で作成したフォルダまで移動する。

ここに、CocoaPods用のファイル「Podfile」を作成する。
    $ vim Podfile
    platform :ios, '6.0'
    target 'Integration Tests', Integration Tests
        pod 'KIF', '~> 2.0.0'
    end
以下のコマンドでインストールする。
    $ pod install
これで、CocoaPods 用のプロジェクトファイルが作成されるので、
作成されたプロジェクトファイルで Xcode で起動する。

4. テスト用プロジェクトの設定
プロジェクトナビゲータから TARGETS で、作成したテストターゲットを選択。

次に、Build Settings をクリックする。
検索窓等を使って Bundle Loader を検索する。
この設定値が空なら、以下の項目を入力設定する。
    $(BUILT_PRODUCTS_DIR)/アプリ名.app/アプリ名
同じく、検索窓等を使用して Target Host に以下の値をセットする。
    $(BUNDLE_LOADER)
次に、Build Phases の Target Dependencies に、テストターゲットを追加する。

最後に、Scheme -> Edit Scheme で、
Build の項目に「+」で、テストターゲットを追加
Test の項目に「+」で、テストターゲットを追加
(この設定で、製品ターゲットを Scheme で選択している状態でも Command + U でテストが走るようになる)

5. テストファイルの作成
ここで、テストを記述するファイルを追加する。
テストターゲットのフォルダに、任意のクラスファイルを作成する。

作成した中でヘッダーファイルには、「KIF/KIF.h」 をインポートする。
    #import <KIF/KIF.h>

    @interface hogeTests : KIFTestCase
    @end
実装ファイルには、以下の内容でメソッドを追加する。
    #import "hogeTests.h"

    // 実行前
    -(void) beforeAll {
    }
    // 都度実行前
    -(void) beforeEach {
    }
    // 都度実行後
    -(void) afterEach {
    }
    // 実行後
    -(void) afterAll {
    }
    // テスト
    -(void) testXXX {
        // Accessibility Label が「SignIn」の UIView をタップ
        [tester tapViewWithAccessibilityLabel:@"SignIn"];
    }
以上で、テストを実行すればテストが動くはず。

ちなみに、1.0 系では KIFTestController を継承して作成するが、2.0 では、このクラスが KIFTestCase になっており、
またシナリオという考えから普通のテストと同じ test~ に変わっている。
シナリオについては、ファイル毎に分ければ良いという事だろうか。

1.0系と、2.0系とのテストケースの記述違いについては、以下を参照

変更点ぽい物
https://github.com/kif-framework/KIF/releases/

あと、UIView などの操作は、「Accessibility Label」 を使用(ラベルを元にコントロールを探す)しているので、
付けていなかった場合はつける事(storyboard で付けるかコードで付ける)。

テストで使えるメソッドについては、インストールされた KIF のソースを見ればメソッドが書かれているので頑張って自分で実装したいメソッドを探し利用する。
また、「SenTestingKit」 も継承しているので、OCUnit での記述はそのまま生かせる。

最後に
感想としては、設定については OCUnit と同じ感じなので普段から利用している人にしたら楽に設定が出来るのではないかと思った。
また、はじめにも書いたがバージョンの違いで全く使い方が違うので迷わない事。
Ver1.0 では、シナリオとか書いてあったのに 2.0 系では全く無くなっていたのは困った。

駆け足で書いて、かつ毎度の事ながらスクリーンショットが無いので把握しづらいとは思う。
とりあえず使える状態に持って行くまでが面倒だが、これも一度設定すれば開発終了まで使えるので、設定の価値は有ると思う。

2013年9月24日火曜日

CocoaPods を使ってみた

ここ最近はテスト関連の情報をよく見る。

そこで、それらのライブラリのインストールには github から取得する方法と、
「CocoaPods」 からインストールする方法が書かれいている。

CocoaPods。。。ここ最近色んな技術の名前を見ているので、消化不良気味で一歩引いていたが、何か良い事有るだろうという事で、調べてみた。

早い話、Xcode でのライブラリ管理が行えると言う物。
Ruby の RubyGem の様な物。

CocoaPods
http://cocoapods.org/

このリンクを見れば、全て書かれてあるが、実際に使ってみないと分からないので、試しに使ってみた。

一緒に参考にしたのは以下のページ

開発レシピ:Objective-Cのライブラリ管理ツール CocoaPods
http://www.iosjp.com/dev/archives/451

これまた、上記のページを見れば終わる(。。。この記事の意味は???)
でも気にしない。
今回は、前回の GHUnit の導入までを試してみる。

1. 前準備
CocoaPods のインストールには ruby(rubygems) を使用する。
ruby(rubygems) を最新の状態でのインストールが良いらしいので、最新の状態にしておく。
$ gem update --system
あと、Xcode の Command Line Tools も必要となるので、インストールする。

2. インストール
以下のコマンドで、CocoaPods をインストールする。
$ gem install cocoapods
インストール完了後は、一旦ターミナルを閉じて再度開き直すと良い。

そのあと、CocoaPods のセットアップコマンドを入力
$ pod setup
余談になるが、以下のコマンドでバージョンの確認が出来る
$ pod --version
3. 対象プロジェクトの作成
CocoaPods でインストールする GHUnit の対象となるプロジェクトを作成する。
今回はテストなので、適当に Xcode から Single View Application を作成する。

次に、テストを格納するプロジェクトを作成
Menu -> File -> New -> Target
作成したプロジェクトから、不要となるファイル(AppDelegate.m/h)を削除する。

プロジェクトナビゲータで、インストールに必要な QuartzCore.framework を追加

ここで、一旦 Xcode を閉じる(不要かも知れない)

4. CocoaPods の設定
上記で作成したフォルダまでターミナルで移動する(xxx.xcodeproj が有るところまで)。
CocoaPods が読み込む 「Podfile」 というファイルを作成する。
$ vim Podfile
platform :ios, '6.0'
pod 'GHUnitIOS', '~> 0.5.5'
5. インストール
Podfile が作成できたら、以下のコマンドによりインストールする。
$ pod install
これで、インストールが始まる。
インストールが完了したら、「xxx.xcworkspace」というファイルが作成されている。
(元々のプロジェクトファイル xxx.xcodeproj とは別に作成されているところに注意) このファイルをダブルクリックして Xcode を起動する。
(ターミナルから 「open xxx.xcworkspace」 とコマンドを打っても開く事が出来る)

6. GHUnit 使用前準備
プロジェクトナビゲータで、Build Settings の Other Linker Flags に 「-ObjC -all_load」 を追加する。

テストプロジェクトに main ファイルを作成する。
    int main(int argc, char *argv[]) {
        @autoreleasepool {
            return UIApplicationMain(argc, argv, nil, @"GHUnitIOSAppDelegate");
        }
    }
7. テストを書く
あとは、テストコードを記述していく。
(CocoaPods のインストールなのでここまでで終了)


注意点:
特に大きくはまった事もなくスムーズに導入できたので良かった。
とはいえ、凡ミスが多少あったので記載します。

引っかかった点としては、
  • Podfile を Profile と間違えていた。
    論外ですな。
    でも、間違ってたんです。。。
  • Podfile の記述で「:」と項目の間にスペースを入れてしまった。
    詰めて記述しないとダメなようです。
    ×「platform : ios」
    ○「platform :ios」
本当にしょうもないミスで恥ずかしい。。。

導入するまでは、また新しい技術かと尻込みしたけど、一度使い方を覚えれば、後の作業に十分対応できてしかも簡単となれば、導入する価値は十分有るなと思いました。

2013年9月23日月曜日

CoreLocation でアプリが落ちる

現在作成している iPhone アプリで、CoreLocation を使用しているが、起動時にアプリが落ちる事がある。
詳細な情報を確認しようと模索するが、どの部分で落ちているかがつかみきれない。
基礎的な部分がまずい気がするので、要調査。

ひとまず、現状での対策ではないかと思われる記事を載せておく。

ipad app running Core Location in background crashes
http://stackoverflow.com/questions/13274148/ipad-app-running-core-location-in-background-crashes

CLLocationManagerDelegate のメソッド内で CLLocationManager インスタンスを release すると EXC_BAD_ACCESS で落ちる
http://d.hatena.ne.jp/glass-_-onion/20110616/1308150765


2013年9月19日木曜日

GHUnit のさわりまで使ってみた

GHUnit の導入までを覚えてとして書いておく。
参考とさせていただいたのは、こちら

GHUnitで単体テストをしてみよう
http://alpha.mixi.co.jp/2012/10858/

そのまんまと言う事と、参考先の方が絵があるのでわかりやすい。
(じゃ何でコレ書くの?となるが、やった記録なので。。。)

1. ファイルの入手
github から
https://github.com/gabriel/gh-unit/downloads

今回は、GHUnitIOS-0.5.6.zip — GHUnitIOS 0.5.6 for iOS (Simulator/Device)を使う。

取得した zip を展開する。
GHUnitIOS.framework というフォルダが出来る。
これを組み込んでいく。

2. ターゲットプロジェクト作成
ここでは、確認用なので適当なプロジェクトを作成する。
Xcode から Single View Application を選択
オプションは好みで(Include Unit Tests は不要)

3. テストプロジェクトの追加
2. のプロジェクト作成後、プロジェクトのページを開き
Add Target を押す。

テスト用のプロジェクトなので、Empty Application で良いので選択する。
プロジェクト名はテスト用とわかりやすいようにした方が良いかもしれない。

これで、ターゲットプロジェクト内に、テストプロジェクトが作成される形となる。

4. テストプロジェクトの不要ファイルの削除
テストプロジェクト内(テストプロジェクトのフォルダ内)は、
  • xxxx-Info.plist
  • xxxx-Prefix.pch
以外は不要になるので、上記以外は全て削除する。

5. GHUnit フレームワークを組み込む
ターゲットプロジェクトの Frameworks のフォルダを右クリックし
Add Files to ... をクリックして 1. で入手したフォルダを指定する。
この時、Destination のチェックは要。
Add to targets には、ターゲット/テスト両プロジェクトを指定する。

これで、Frameworks のフォルダに GHUnitIOS.framework が追加される。

6. GHUnit を使ったコンパイル時の設定
プロジェクトのページの TARGET で、テストプロジェクトを選択し、
Build Settings の Other Linker Flags の項目に 「-ObjC」 を追加する。

7. テストメインファイルの作成
テストプロジェクトに、main となるファイルを作成する。
クラスファイルで良い。
名前は任意(SampleTestMain.m/h)
    
    #import <Foundation/Foundation.h>

    @interface SampleTestMain : NSObject

    @end
   
    #import <GHUnitIOS/GHUnit.h>
    #import <GHUnitIOS/GHTesting.h>


    void exceptionHandler(NSException *exception) {
    //    NSLog(@"%@\n%@", [exception reason], GHUStackTraceFromException(exception));
        NSLog(@"%@\n%@", [exception reason], [GHTesting descriptionForException:exception]);
    }

    int main(int argc, char *argv[]) {
        NSSetUncaughtExceptionHandler(&exceptionHandler);
        
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        
        int retVal = 0;
        if (getenv("GHUNIT_CLI")) {
            retVal = [GHTestRunner run];
        } else {
            retVal = UIApplicationMain(argc, argv, nil, @"GHUnitIPhoneAppDelegate");
        }
        
        [pool release];
        return retVal;
    }
GHUStackTraceFromException(exception) でエラーになるので変更した。

8. テストプロジェクトの確認
この状態で、テストプロジェクトを Scheme から選択して実行すると、
GHUnit 用のアプリが立ち上がるはず(テーブルの画面)

9. テスト対象メソッドの作成
ターゲットプロジェクトに、テスト対象となるクラスを作成する。
今回は、割り算して結果を切り上げる動きをするクラス Calc を作成。
    #import <Foundation/Foundation.h>

    @interface Calc : NSObject

    + (NSInteger)divideRoundUp:(NSInteger)numerator denominator:(NSInteger)denominator;

    @end
    #import "Calc.h"

    @implementation Calc

    + (NSInteger)divideRoundUp:(NSInteger)numerator denominator:(NSInteger)denominator {

        CGFloat value = (CGFloat)numerator / (CGFloat)denominator;
        return floor(value);

    }

    @end
10. テスト用クラスの作成
テストプロジェクトにテスト用のファイルを作成する。
今回は、上記の Calc をテストするので CalcTest と言う名前のクラスを作成
    #import <Foundation/Foundation.h>

    @interface CalcTest : GHTestCase{
        
    }

    @end
    #import <GHUnitIOS/GHUnit.h>

    #import "CalcTest.h"
    #import "Calc.h"

    @implementation CalcTest

    // デフォルトはNOですが、UIのテストやメインスレッドに依存するテストを行う場合はYESにします。
    - (BOOL)shouldRunOnMainThread {
        return NO;
    }

    // 本クラスが実行される前に呼び出されます。
    - (void)setUpClass {
    }

    // 本クラスが終了された後に呼び出されます。
    - (void)tearDownClass {
    }

    // 本クラスの各メソッドが実行される前に呼び出されます。
    - (void)setUp {
    }

    // 本クラスの各メソッドが終了された後に呼び出されます。
    - (void)tearDown {
    }

    // 「test~」というメソッド名にすることでテスト対象一覧に出力されます。
    - (void)testDivideRoundUp {
        NSInteger num = [Calc divideRoundUp:100 denominator:3];
        GHAssertEquals(num, 34, @"test");
    }

    @end
11. テストの実行
テスト記述ができたら、テストのプロジェクトを動かすとテストが動く
右上の Run ボタンを押しても良い

12. わざと間違う
間違えの検出ができるか確認するために、わざと間違えて確認する。
    #import "Calc.h"

    @implementation Calc

    + (NSInteger)divideRoundUp:(NSInteger)numerator denominator:(NSInteger)denominator {

        CGFloat value = (CGFloat)numerator / (CGFloat)denominator;
    //    return ceil(value); ここを変える
        return floor(value);

    }

    @end
この状態で、再度実行するとエラーが出るはず。

エラー内容については、シミュレータ側に表示される。

これで、とっかかりまではできたので作っていくだけ。

2013年9月13日金曜日

UILabel のタッチで Safari を起動

HTML で言うところの A タグを実現する方法。
ラベルや画像にはアクションが付くものと思い Storyboard から Action で繋げようと頑張ったが、出来なかったので方法を調べてみた。

どうやらラベルや画像のコントロールの userInteractionEnabled プロパティを有効(YES)にすることによって、タッチ時のイベントが取れるようになるようだ。
ただ、これだけではイベント受け取り側ではどのコントロールのイベントが判断出来ないので、判断材料するため tag プロパティに値を設定して、コントロールの判断をするようにする。

あとは、決めうちにはなるが、表示する URL を URLスキームを使用して Safari を起動するようにすれば良い。


- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    // ラベル
    myLabel.userInteractionEnabled = YES;
    myLabel.tag = 10;
    // 画像
    myImage.userInteractionEnabled = YES;
    myImage.tag = 20;
}

// タップイベント判断
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    UITouch *touch = [[event allTouches] anyObject];
    if (touch.view.tag == myLabel.tag) {
        [self tapLabel:myLabel];
    } else if (touch.view.tag == myImage.tag) {
        [self tapImage:myImage];
    }

}

// ラベルタップ処理
-(IBAction)tapLabel:(id)sender {

    NSLog(@"label tap!!");
    // タップ元はこれで取得できる(今回は別に使わないが・・・)
    UILabel *label = (UILabel *)sender;

    // ここでは Google を起動
    NSURL *url = [NSURL URLWithString:@"http://www.google.com"];
    [[UIApplication sharedApplication] openURL:url];
 
}

// イメージタップ処理
-(IBAction)tapImage:(id)sender {

    NSLog(@"Image tap!!");
    // タップ元はこれで取得できる(今回は別に使わないが・・・)
    UIImage *image = (UIImage *)sender;

    // ここでは メーラを起動
    NSURL *url = [NSURL URLWithString:@"mailto:xxxx@xxxxx.xxx"];
    [[UIApplication sharedApplication] openURL:url];

}

これで少し、ラベル・画像が便利になった。

2013年9月12日木曜日

NSArray を使った NSDictionary でのソート

CoreData を使っているが、リレーションをしている状態でソートのかけ方が分からなかった。
とりあえず、CoreData については、今後のスキルアップにより方法が見つかるだろうと信じ、別の方法を取る事を考えてみた。
今回は、必要データだけを取り出してソートをかける方法が思いついたので、その方法を取ってみる。

この場合は、NSArray に NSDictionary 型のデータを格納し、NSSortDescriptor を使用する事により、
NSDictionary に格納しているデータをキーとして並び替えが行える事が分かったので実装してみた。

コードは以下の通り
    // 並び替えをするデータを用意
    NSArray *dataArray = [NSArray array];
    for(int i=0; i<5; i++) {
        
        NSDictionary *mdic = [NSDictionary dictionaryWithObjectsAndKeys:
                              [NSString stringWithFormat:@"%d",i], @"index",
                              10-i, @"count"
                              , nil];
        dataArray = [dataArray arrayByAddingObject:mdic];
    }
    
    NSSortDescriptor *sortDescCount;
    sortDescCount = [[NSSortDescriptor alloc]initWithKey:@"count" ascending:NO];

    NSArray *sortDescArray;
    sortDescArray = [NSArray arrayWithObjects:sortDescCount, nil];
    
    NSArray *sortArray;
    sortArray = [dataArray sortedArrayUsingDescriptors:sortDescArray];

    // 内容確認    
    for(NSDictionary *dic in sortArray) {
        NSLog(@"sort data count = %@, index = %@", [dic objectForKey:@"count"], [dic objectForKey:@"index"]);
    }

一応ログ出力で、問題ない事を確認した。
上記は、並び替えのキーが一つだが、複数キーも対応できる様だ。
処理の幅が広がるね。

2013年9月7日土曜日

Ruby で APNS を行う(houston)

今日は、Ruby を使って、APNS(Apple Push Notification Service)を行ってみたいと思います。

今回も 1 から作っていくのではなく、mattt さんが作成してるライブラリの力を借りる事にします。
ライブラリの名前は、
houston[ヒューストン] (https://github.com/nomad/houston)

名前の由来等サイトに書かれている気がしますが、、、とりあえず作業に入りたいと思います。
あと、APNS での Push の仕組みなどは Apple のドキュメントを参照してください。

Apple Document: Local Notification および Push Notification プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/RemoteNotificationsPG.pdf

また、今回は完全なシステムを立ち上げるのではなく、あくまで手動で huston を使って Push の確認を取るところまでを記載します。
言うまでもなく実システムに組み込む場合は、細かな処理に手を入れる必要があります。

また、今回は色々なサイトを見させてもらったのですが、PHP で Push を実現する事が出来る apns-php を使っておられる方が非常に分かりやすく解説されており、非常に勉強になりました。
houston 以外の部分(houston もほとんどサイト丸写しなんですけどね。。。)は、こちらのサイトを見たほうがわかりやすいかもしれません。

MonteCut さん
iOS + PHPでPush Notificationを実装する


以下が、作業内容です。
なんども言いますが、1.〜 4. までは確実に
iOS + PHPでPush Notificationを実装する
の方がわかりやすい。。。

1.iOS Developer から Push 用の IDs の取得を行う
Push を行うに当たり、端末の識別番号を取得するなどの処理を行うiPhone側(iOS)プログラムが必要となります。
このプログラムを作りに当たって ID を取得します。
自分は、これまでに IDs の登録時のApp ID Suffix の Bundle ID をワイルドカードの物(Wildcard App ID)で賄っていました。
しかし、Push を行うにはこの形式の物は使えないと、色々なサイトに書かれてあったので、Push テスト用に別途作成しました。


2. 証明書の取得
1. で作成した IDs から証明書ファイルを取得します。
取得した証明書をダブルクリックでキーチェインアクセスへ登録します。

登録後、p12 ファイルを取得します。
取得方法は、キーチェインアクセスで、今回追加した証明書を選択し(キーチェインを「ログイン」、分類を「自分の証明書」を選択すると出てくる)、左の三角を押して、自分の鍵を出した状態で、Ctrlキーで両方選択し、右クリックで p12 形式で書き出します。
(今回名前を AppNameDev.p12 とした) この p12 から pem ファイル形式へ変換します。
$ openssl pkcs12 -in AppNameDev.p12 -out AppNameDev.pem -nodes
そしてその pem ファイルを houston で使用・指定します。

3. プロビジョニングファイルの作成
これも1. 同様に Push で確認を行うためのプログラムを実機で試すために必要なプロファイルです。
どうやら、Push の確認については、実機でないと行えない様なので、これも iOS Developer で取得しておきます。

iOS Developer から Provisioning Profiles と辿り「+」ボタンで作成していきます。
Development は iOS App Development として、Select App ID は、1. で作ったアプリを選択します。
Select certificates. は、自分の証明書を選択
Select devices. は、動かす端末を選択
Name this profile and generate. は、任意のプロファイル名を入れ Generate で作成後、ダウンロードします。
ダウンロードしたものをダブルクリックでオーガナイザに登録します。

4. iOS で Push 対応のプログラムを作成する
XCode を立ちあげ、適当なプロジェクトを選択(Empty Applicationでもいい)し、プロジェクト設定画面を開きます。
PROJECT で、プロジェクトを選択し、Build Settings を選択し、Code Signing の項目の Code Signing Identity の Debug 用プロファイルを 3. で登録した物に設定します。

次に、プログラムを書いていきます。
...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    ...

    // アプリケーションが起動するたびに、デバイストークンを要求してそれをプロバイダに渡すことで、
    // プロバイダが最新のデバイストークンを持つことを保証
    [application registerForRemoteNotificationTypes: (UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeSound|UIRemoteNotificationTypeAlert)];
    
    return YES;
}

- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *) devToken
{ 
    NSLog(@"deviceToken: %@", devToken);
    // 自分のサーバーにトークンを送信する
}

- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *) err
{ 
    NSLog(@"Errorinregistration.Error:%@", err); 
}
実装が問題なくでき、ビルドも通ったら、実行してみてください。
上手くいけば、デバイストークンが取得できるはずです(ログに出力されている)。
ここで、表示されるデバイストークンをメモっておきます。

実際には、ここで取得したトークンを、サーバへ送信して、サーバー側で値を管理する様にしないと行けません。

5. Ruby(huston) のプログラムを作成する
ここでようやく houston の出番です。
houston のサイトから zip ファイルを取得します(git で取得しても良い)
取得したファイルを展開し、任意の場所へ移動します(以降 houstonフォルダとする)。

houstonフォルダ内に、証明書を入れるフォルダを作成します(ここでは、certificateフォルダとする)。
作成したフォルダに、2. で作成した証明書ファイルを入れます。

次に、houstonフォルダに、Push を実行するためのスクリプトファイルを作成します(名前は任意。今回は pushTest.rb とした)
パスの関係上で、Web のサンプル通りだとエラーになったので、各プログラムの読み込むパスを変更しました。
require './lib/houston'  ← ./lib/ を追加
# Environment variables are automatically read, or can be overridden by any specified options. You can also
# conveniently use `Houston::Client.development` or `Houston::Client.production`.
APN = Houston::Client.development
APN.certificate = File.read("./certificate/AppNameDev.pem")

# An example of the token sent back when a device registers for notifications
token = "<4.で取得したトークンを貼り付ける>"

# Create a notification that alerts a message to the user, plays a sound, and sets the badge on the app
notification = Houston::Notification.new(device: token)
notification.alert = "Hello, World!"

# Notifications can also change the badge count, have a custom sound, indicate available Newsstand content, or pass along arbitrary data.
notification.badge = 57
notification.sound = "sosumi.aiff"
notification.content_available = true
notification.custom_data = {foo: "bar"}

# And... sent! That's all it takes.
APN.push(notification)
lib フォルダの houston.rb も同様に
module Houston
  VERSION = "0.3.0"
end

require './lib/houston/client'        ← ./lib/ を追加
require './lib/houston/notification'  ← ./lib/ を追加
require './lib/houston/connection'    ← ./lib/ を追加
ここまで、できれば後は実行するだけです。
$ ruby pushTest.rb
これで、iPhone に Push 通知が飛んできます。

駆け足で書いているため(絵がないからわかりづらい)、抜けているかもしれませんが、また自分でも使うことがあると思うので、その際に修正していきたいと思います。



番外
この huston も、先日のdubai と同様、bin フォルダに apn と言うプログラムが用意されています。
これを使えば、コマンドラインから Push を行う事が出来ます。
(パスは自分の環境に対応してください)
$ apn push "4.で取得したトークン" -c /certificate/AppNameDev.pem -m "Hello from the command line!"

用途に合わせて、使い分けをすれば、軽作業で Push 処理が実現できるかと思います。


2013年9月5日木曜日

Passbook 使っていますか?

iPhone にデフォルトrで入っているアプリに 「Passbook」 と言う物がある。

iOS 6 のPassbook の紹介




実際に、iPhone を所持しているがこのアプリの意味(存在意義)がよく分からなかった。

売りしては、
  • クーポン券や搭乗券などが一括管理できる。
  • 場所や、日時指定によりサーバーから更新とプッシュ通知が出来る。
と言う事らしい。
PCやスマホを使っている人にすると、「で?」という反応しか返ってこない。

つまり、わざわざ専用のアプリで管理する必要無いじゃないかと言う事らしい。
Safari(Web) があれば良いだろと。

確かに。
メールとWebを組み合わせれば、一括管理も出来無くないし、位置情報や日時についても、
今日日のHTML5等々を駆使しすれば出来ない事はない。

じゃ、なぜ Apple がこんな物をと思ったが、要するに「プッシュ通知」が出来る点と、情報を見せるだけのアプリなら「オリジナルでアプリ組まなくても良いよ」ということを売りにしたいのではないか?と思っている。

あと、よく分からないけど、惹かれる物がある。
子供の頃にカードとか集めてた時の楽しい気分が有るからだろうか?

心配なのは、対応しているサイトのない事。。。

Apple 好きの人曰く、Apple は時折使われないようなアプリを平気で入れてくると言っていた。
Passbook が該当しない事を期待しつつ。

何か出来ないか模索したいと思っている。

2013年8月31日土曜日

iPhone で Box2D を使う

iPhone で、物理演算を行うためのライブラリ(?) Box2D を動かす所までを調査してみた。
前回は、ソースの取得で苦労した訳だけど、要る物さえ取れてしまえば後は楽か・・・と思ったが甘かった。

今回は、以下のサイトを参考にさせて頂いた。

UIKit で物理演算エンジンを使用する
http://qiita.com/shu223/items/8a1d064411a2a122983d

ここでは、自分が行った点を補足として残す。

まず、1.のファイルの取得については、前回の記事で書いたとおり SVN にプロキシ設定を行って取得した。

次に、4.の必要ファイルの拡張子を cpp から mm に変更する所。
これは、ターミナルを使い対象(Box2D/Box2Dの中)の各フォルダごとに変換を行った。
for nm in *.cpp; do[Enter]
> mv $nm ${nm%.cpp}.mm;[Enter]
> done[Enter]
上記が、フォルダの中の cpp ファイルを取得して mm に置き換えるスクリプトである。
フォルダ数も知れていたので、手作業で行ったが、結構神経使ったので、もう少し改良した物を使った方が良いかもしれない(シェルに明るければ簡単にできるんだろうな)。

作業が前後するかも知れないが、拡張子を変換してから XCode に放り込んだ方が良さそうだ。
放り込んだ時点で XCode では、警告が沢山出た。

warning: Missing file:/job/hoge/trunk/hoge/Classes/Generic/UI/UICustom/NSCViewVisitor.h is missing from working copy

(警告メッセージが違うかもしれん。。。)
このメッセージは何かというと、SVN 管理のファイルが変更になっているぞ!と言う物らしい。

内容的に、無視しても良いけど、気になるので(目障り)、監視ファイルを削除することにした。

コピーしたフォルダ内に「.svn」という隠しファイルがあるので、削除すればこの警告は消える。

これで、中間地点となる5.のヘッダーファイルをインポートしてビルドをする部分までは行ける。
と、思ったがエラーが出る。

「Lexical or Preprocessor ‘cassert’ file not found」

い、意味不明だ・・・
調べたところ、関係する拡張子が「mm」でないとダメらしい。

上記で変換してるんだけど・・・と思ったが、これの影響範囲は関連するプロジェクト全てのファイルが対象の事だった。
つまり、「ViewController.m」 も 「ViewController.mm」 にしなくては行けなかった。
これで、ビルドまで通るようになる。

ここまで来ると、あとはそのまま実装していけば完了するはず。
本当にコピペとなるためにコードは省くが、ビルドして実行すると、箱がポロポロ落ちてくるサンプルが作れる。

今の段階では、これで終了したが、ゲームなりなんなりに使うためには、もう少し使い方を勉強する必要がある。
先が長そうだったので、余裕が出たときに再度取りかかろうと思う。