2015年10月19日月曜日

AppBank GAMESを退職していました

表題の件、私akisuteは2013年10月末日を持ちましてAppBank GAMES株式会社を退職したことをご報告いたします。短い間ではございましたが関係者皆様大変ありがとうございました。今後の予定につきましてはとりあえずのところ問題なくやっていけそうで助かっております。

みなさま3年間お疲れ様でした\(^o^)/

※すみません、なんかTwitter経由なりLINE経由なりで皆様から消しといたほうがよくねというご指摘を多数頂いておりまして、別に意地張るものでもないので以前の内容は削除させていただきました(´・_・`) 現職の会社から何か言われたってことはないのでごあんしんください\(^o^)/

2015年9月7日月曜日

Apple Watch アプリの実機インストールが失敗する時のメモ

Apple Watchアプリの実機インストールが失敗する際にチェックする事項をまとめてみました。Apple Watchアプリが実機インストールに失敗する原因は多岐にわたり、ここにまとめてある内容だけではとても網羅しきれませんが、何かの参考になれば幸いです。

以下、すべてwatchOS 1での結果です。watchOS 2ではまた何かが変わっているかもしれませんが、基本的なところは同じかと思います。

最初にやること

いずれの場合でもまず最初に必ずやることとして、XcodeのDeviceウィンドウを開いて実機のログを確認するようにしましょう。このログにApple Watchアプリをインストールするときのログも全て残るようになっていますので、一番の手がかりになります。

症状: 「インストールに失敗しました」ダイアログ

このダイアログが表示されるときは、殆どの場合iPhone本体にインストールされるApple Watch App ExtensionとApple Watch上にインストールされるApple Watch Appの両方がインストールに失敗していることが多いです。

失敗の理由ですが私が遭遇したものの中だとダントツ多かったのがコードサインエラーです。どうしてもインストールが成功しない場合は、Xcodeのビルド設定などから使用するProvisioning Profileを明示的に指定してやるとうまくいくことが多かったです。

このとき指定するProvisioning Profileは、Debug向けビルドであれば標準のデバッグ用のiOSTeam Provisioning Profile: *を使うか、または専用に用意したcom.example.myapp.watchkitappないしcom.example.myapp.watchkitextensionのようなApp IDを使うプロビジョニングを選びましょう。Release向けビルドの場合は必ず専用に用意したApp IDを使うプロビジョニングを選択する必要があるので、面倒でも必ずApple Watchアプリを作る際には新しくApple Watch App Extension用のApp IDとApple Watch App用のApp IDを作るようにしましょう。

症状:「インストール中です」が終わらない

このようなケースの場合は、どうやらiPhone本体にインストールされるApple Watch App Extensionのインストールには成功しているのだけれども、Apple Watch上にインストールされるApple Watch Appのインストールが何らかの理由で失敗している事が多いようです。

私が遭遇した失敗の理由ですと、例えばApple Watch AppのInfo.plistに不要な値を記載してしまったためにインストールにコケたということがありました。具体的にはLSApplicationQueriesSchemesとNSAppTransportSecurity(いずれもiOS 9向けのキー)をApple Watch AppのInfo.plistに記載していたのですが、インストール時にこれらが
(Error) WatchKit: validateWatchKitApplicationInfoDictionary, invalid Info.plist key 'LSApplicationQueriesSchemes'
のように言われて失敗してしまっていました。

watchOS 1の地点ではインストール時にかなり厳密に値をチェックしているようなので、Apple Watch App向けのInfo.plistには余計なものを記載しないほうがよさそうです。watchOS 2ではまた変わるかもしれませんが・・・

症状: インストールは正常に完了するのにアプリが全く立ち上がらない

大変稀なケースですが、インストールは完了するのにアプリが全く立ち上がらず、最初のアプリ名が表示されてクルクルが回ったまま永遠に先に進まなくなってしまう事があります。私が遭遇した場合の原因ですが、Entitlementsの指定が間違っていたときにこのような問題が発生したことがありました。通常アプリのEntitlementsは自動的にXcodeが生成して付与するため問題にはならないのですが、何らかの理由で自分でApple Watchアプリ向けにEntitlementsを自作して設定した場合は大変問題になります。

具体的には、Apple Watchアプリ向けにEntitlementsを自作する場合には必ず以下のようにしてください。

  • Apple Watch App Extension向けのEntitlementsには、必ずapplication-identifierとkeychain-access-groupsを含める。
  • Apple Watch App向けのEntitlementsには、application-identifierとkeychain-access-groupsのみを含め、その他のキーは絶対に指定しない。

application-identifierは指定がないとそもそもApple Watchアプリのインストールに失敗します。

keychain-access-groupsはアプリ内でKeychainを一切使っていなくても必ず含めてください。どうやらiPhone本体とApple Watchアプリ間の通信の際にこのEntitlementsをこっそりシステムが使用しているようで、keychain-access-groupsが指定されていないと冒頭で紹介したような事態が発生するようです。エラーメッセージも何も一切表示されなかったため調査に大変苦労しました。

2015年7月31日金曜日

iOS 9 の split screen に対応するサンプルプロジェクトを作成してみました

WWDCでの発表でもありましたとおり、iOS 9からはiPadで画面を二分割して複数のアプリケーションを同時に実行することができるようになります。このSplit Screen機能に対応するためには、iOS 8で導入されたAdaptive User Interfaceを活用する必要があります。

参考:



Adaptive User Interfaceな実装をする上で最も簡単な対応方法はひとつのUIViewControllerで複数のSize Classeに対応することです。大体のケース、例えばApple標準のMapアプリやメールアプリなどはこれで問題がないのですが、例えば横方向がRegularサイズの場合(=iPadやiPhone 6 plusなど横に広いデバイスの場合)はUICollectionViewを使ってタイル上に広くアイテムを並べ、横がCompactなデバイス(縦向きのiPhoneなど)の場合はUITableViewを使って縦に多数のアイテムを並べたいという需要があったりします。この場合、ひとつのUIViewControllerでどちらにも対応するのがかなり困難になり、シンプルな実装になりません。

そこでSize Classに応じてdynamicに複数のUIViewControllerを差し替えて表示するサンプルを作ってみました。



特徴:

  • Size Class変更時にきちんとアニメーションします
  • 当然Split Screenにも対応しています


iOS 9は既存のコードをAdaptive User Interfaceに対応させる良い機会だと思いますので、これを機会に皆さんも自分のアプリを見なおしてみてはいかがでしょうか?

2015年4月10日金曜日

Apple Watch の実機を触ってわかった、アプリ開発者が抑えておくべきポイント


本日からついにApple Watchの実機がお目見えとなりました。私も早速Apple Storeに行って試着・試用してきたのですが、予想以上にアプリ開発に影響がありそうな点が多数見つかりましたので、思うところをブログ記事にまとめて公開しようかと思います。

■小さい、とにかく小さい

Apple Watchの実機を身につけてまず最初に感じるのがその圧倒的な小ささです。この小ささというのは

  • これまでのAndroid Wearデバイスのどれと比べても感じる相対的な小ささ
  • Apple Watch上で表示されているUIを見て感じる絶対的な小ささ

の2つの要素から感じられます。

試しに私が身につけているAndroid WearデバイスとApple Watch Standard 42mmを並べて写真をとってみたのですが、見ての通り42mmモデルですら表示領域がずいぶんと小さいのがわかります。

その上Apple WatchのUIは全体的にAndroid WearのUIと比べて密度が高い用に感じられます。こちらのブログに具体的な例があるのでぜひ参照していただきたいのですが、見ての通り同じアプリでもApple Watchのほうが詰まったUIになっています。ただでさえデバイス自体が小さめな上に密度の高いUI、具体的にはアプリ内で常時上にナビゲーション領域が表示されたりする、ということでなおさら小ささが際立っているわけです。

小ささをより体感するために、iPhone 6の画面でApple Watch 38mmの画面サイズを表現してみました。

アイコンが一つにラベルが一つプラスアルファ程度がせいぜいの大きさしか無い、というのがよく分かるかと思います。

したがって繰り返し繰り返し随所で述べられていると思いますが、画面上に表示する要素は徹底的に少なくする必要があります。私も十分に少ない要素だけを画面に表示するように心がけていたつもりでしたが、実際にデバイスに触れたあとに見返すとまだまだ要素が多すぎるぐらいです。少なすぎるのではと心配になるぐらいまで減らしてちょうどいいのではないでしょうか。

Appleの標準のアプリなどでかなり高密度なUIを採用しているものもありますが、そこは真似しないほうが良いと考えています。具体的には標準のマップアプリなどは38mmモデルの上では細かすぎて地図を読み取るのが極めて困難でした。

■Glanceこそがすべて

Apple Watchのインターフェースのナビゲーションは以下の図のようになっています。

基本は時計フェイスが表示されていて、そこから竜頭を押すとHomeに遷移してアプリを選択して起動することができます。時計フェイスを下にスワイプすると上からNotification Centerが表示され、上にスワイプすると下からGlanceが表示されます。Glanceは左右スワイプで次々に閲覧することができます。感覚的にはGlanceはiOSデバイスにおけるWidgetのようなもので、常時Widgetが時計フェイスの下に並んでいるようなイメージをするとわかりやすかったです。

このインターフェースの中でアプリができることで、最も重要になってくるのがGlanceです。操作してみてわかったのですが、Home画面からアプリを起動するのはただでさえ小さいWatchの画面上に無数の小さいアイコンが並ぶため困難苦痛を極めます。したがって必然的にアプリの状態を確認したりアプリを起動するのはGlanceを使うのが最も楽でスピーディで良いということになります。GlanceこそがApple Watchアプリにおけるすべての窓口と言えそうです。ここをどれだけ便利に使いやすく見せるかによってアプリの価値が変わってくるかもしれません。

■ネイティブアプリは速いが転送は遅い?

気になるApple Watchの動作速度ですが、まず通常の用途ですとかなりサクサクと動作しました。時計フェイスから通知センターに遷移したりGlanceを見たり、Glance間を切り替えたりするぶんには素晴らしい応答速度で、手元のAndroid Wearデバイスよりも機敏に感じました。

これがアプリとなってくるとだんだんと遅さが感じられる場面が出てきます。気になった点としては、

  • いくつかのGlanceについてロードが終わらない、ないしロードが遅すぎる。マップ・天気・株価が該当。
  • GlanceまたはHomeからのアプリの起動が遅いときがある。フィットネスで該当。
  • フィットネスで「開始」ボタンを選択してから実際に開始するまでに明らかに感じられる遅れがあった。
  • マップアプリについてはロードが遅く、地図が表示されるのも遅い。

これらから推測するに、おそらくネイティブでアプリが動作している部分に関しては素晴らしいパフォーマンスが得られているものの、本体側のiPhoneからデータを転送している箇所に関しては顕著にパフォーマンスが落ちているのではないか思われます。

今回体験する事ができた実機にはサードパーティ製のアプリが入っていなかったので、我々開発者が作ったアプリに関してどの程度のパフォーマンスが得られるのかは全く不明ですが、この調子ではあまり良い結果が得られないかもしれません。今後のSDKの拡張でApple Watchネイティブのアプリが作れるようになるまでは、本体からデータを転送する頻度および転送量を少しでも削減できるようなつくりを目指すしかなさそうです。

■妄想とか将来の話

その他現状はサードパーティ開発者からは使えないのですが、将来的に面白くなりそうだと思った点を挙げます。

まず竜頭コントロールについてですが、現状竜頭コントロール入力をアプリ側から取得することができないのはみなさんご存知のとおりです。触った感じ竜頭自体は非常に良く出来ていたのですが、画面のどの箇所が竜頭でコントロールできるのか出来ないのかがいまいちよくわからないという問題があるように思えます。最も顕著な例は時計フェイスのカスタマイズUIで、これはカスタマイズする箇所をタップしてから竜頭で項目を選択するという仕組みになっているのですが、直感的に非常にわかりづらかったです。きちんと考えて統一的に使われていれば便利かもしれませんが、画面のタッチとの併用がほぼ必須なため竜頭だけでコントロールできなかったりなど、課題が山積みのように感じます。

逆にForce Touch(強く力を込めて押しこむようにタップする)機能ですが、これは非常に優れているように感じました。ダブルタップと特性は似ていますが、精密動作を必要とせずTapticエンジンによるフィードバックによって入力成功が伝わる点を考えてもダブルタップより圧倒的に優れている入力方式と言えます。現状Force Touchは自由に使うことができずメニューの表示用途に限定されていますが、これはまずForce Touchという操作の存在を確実にユーザーに理解してもらうという意味で良いと思います。この操作が広まればゆくゆくはiPhoneやiPadにもForce Touch搭載されることが確実でしょうし、アプリは積極的にForce Touchを取り入れていくべきと思いました。

将来的にForce TouchがiPhoneに導入されるとなると、iOSのAndroidに対する現在の弱点であるメニューボタンの不在を補う重要な役割になってくるかもしれませんね。さらにゲームでも大変有効に使える入力方式に間違いありません!夢が広がります。

Force Touchといえばその対となるTapticエンジンも非常に素晴らしかったです。Force Touch時のフィードバック、通知時のフィードバック、友人へのハートビートの送信、すべてで全く異なる触覚が伝わってくるのがまさに見事でした。まったく画面を見なくても触覚の違いだけで何が起こっているのかを判別できるほどです。現状Tapticエンジンを自由に触ることはできませんが、もし開放されたらTapticからのフィードバックだけで画面を全く見ないでも十分に使えるアプリが作れるかもしれません。全くユーザーを煩わせることない究極のUIになりうるかもしれませんね。

最後にGlanceについてちょっと触れます。基本的に現在サードパーティのアプリがGlanceでできるのは情報を表示するだけで、Glanceがタップされた時の挙動もWatchアプリが起動するだけに固定されてしまっています。

ところがAppleのネイティブアプリである心拍数Glanceについては、なんとGlanceが表示された瞬間に心拍数が自動的に計測開始され、さらにボタンをタップするとダイアログが表示されるというつくりになっていました。すなわち機能が開放されていないだけでリッチなGlanceを作ることも可能なようです。先にGlanceこそがすべてだと述べましたが、このリッチなGlanceを作る機能が開放されたらApple Watchのサードパーティアプリの可能性は更に広がると思います。

2015年3月16日月曜日

WatchKit 向けの UIImage Animation を簡単に実装するためのライブラリ ParaMangar を作りました

いよいよApple Watch発売日まで1ヶ月ということで皆様精力的にWatch向けのアプリを作成されているのではないかと思いますが、現状のWatchKitで誰もが一度はマジギレ不満に思う点がアニメーションです。

こちらのブログにある通り、WatchKit向けのアニメーションを作成するのは現状極めて面倒と言わざるを得ません。

http://d.hatena.ne.jp/shu223/20150214/1423901142

そこでiOS側でUIViewを今までどおりレンダリングして、その結果をファイルにしたりUIImageにしてWatchKitに渡せばいいじゃない!というライブラリを書いてみたので公開いたします。

https://github.com/akisute/ParaMangar

このParaMangarを使うとこんな感じでアニメーションが作れます。


ライセンスはMITです。
あまりテスト出来ていないのでバグだらけかもしれませんが、ご意見issueなどgithubでお寄せいただければ幸いでございます!!

2015年2月2日月曜日

YouTube などのフルスクリーンで再生される UIWebView の動画をプログラムから終了させる方法

UIWebViewでyoutube.comやvine.coなどのサイトの動画を開いた場合、動画がフルスクリーンの専用動画プレイヤーViewControllerで再生されることがあります。このフルスクリーン動画を閉じる方法です。

方法1: 安全な方法

JavaScriptのFullscreen APIを使って安全に閉じることができます。iOS 6以上で動作確認済みです。

Fullscreen APIについては以下の資料が詳しいです。


方法2: animated, completionの制御もしたい場合

ここからがお待ちかねの黒魔法になります。
先ほどのJavaScriptを使った方法ではフルスクリーン動画を閉じる際のアニメーションを制御できません(必ずアニメーションが発生します)。またフルスクリーン動画を閉じ終わったタイミングのcompletion handlerが存在しません(JavaScriptでハンドルしようにも、videoタグのonendedイベントは通常の再生終了時にもイベントが飛ぶ上にアニメーション終了時ではなく再生終了時にイベントが飛ぶため使いづらい)。

通常はこれらが問題になることはまずありませんし、万一あったとしても仕様のほうを変えるほうが適切ですが、何らかの止むに止まれぬ理由によりなんとかしなければならなくなる場合が稀によくあります。

そこで画面上にUIWebView経由で表示されている動画プレイヤーViewControllerをビュー階層をたどって見つけ出し無理矢理dismissViewController:completion:で消すという方法を取ります。iOS 6以上にて動作確認済みです。ただしiOS 7以下の場合とiOS 8の場合で全く構造が異なり、将来にわたって動作するか非常に怪しいです。

Private APIの名前がバリバリ含まれるコードになっておりますので審査に出すアプリには使用しないことを強くおすすめさせていただきます。

2015年1月15日木曜日

ReactiveCocoa を Swift から使ってみた(2) KVO編

前回の記事から引き続き、ReactiveCocoaを触ったりしています。FRPの概念に慣れてくると通常のプログラミングスタイルでは得られない知見に遭遇出来てなかなか面白いです。

今回はまずSwiftでReactiveCocoaを学ぶときに参考にするドキュメントについてご紹介したいと思います。といってもSwiftに特化したドキュメントはほとんど存在しないため、Objective-CでReactiveCocoaを学ぶときのドキュメントを見て学ぶしか無いのが現状です。

私は個人的にはサンプルコードを直接触るほうが性に合っているようなので、以下のオープンソースのReactiveCocoaで作られたアプリのコードを見ています。
https://github.com/AshFurrow/C-41
https://github.com/jspahrsummers/GroceryList
GroceryListのほうがより本格的にFRPっぽい書き方をしているのでオススメです。実際にコードを動かしたい場合はC-41のほうが比較的簡単に動かしやすいと思います(それでも使われているライブラリが古いためビルドを通すのが大変だったりしますが・・・)

SwiftでReactiveCocoaを使っている例としてはCarthageが良いと思います。ただしMacのコマンドラインアプリなのでUIまわりがらみのサンプルとしては余り参考になりませんでした。
https://github.com/Carthage/Carthage

例えばReactiveCocoaを使う上でどうしても欠かせないのがRACObserveを使った特定プロパティに対するKVOのシグナル化です。これにより特定のプロパティが変化した時にシグナルを受け取ることができるようになります。通常Objective-CでReactiveCocoaを使う場合は標準のRACObserveというマクロを使用すれば良いのですが、Swiftではマクロが使用できないため他の方法を採用する必要があります。

調べてみたところ、幸いにしてRACObserveマクロは単なるNSObjectのrac_valuesForKeyPathメソッドのラッパですので、以下のようにしてrac_valuesForKeyPathを使用すれば解決できます。


ここで注意することとして、KVO対象となるプロパティにはdynamic修飾子を付ける必要があります。Objective-Cにもdynamic修飾子はありましたが、Swiftのdynamic修飾子はObjective-Cのものとは意味が異なりdynamic修飾子を付けたプロパティについてObjective-Cと同様に動的プロパティアクセスを行うようにする(具体的にはobjc_msgSendする?)という意味があります。詳細については以下の記事を参照してみてください。
http://stackoverflow.com/questions/24092285/is-key-value-observation-kvo-available-in-swift#comment39273366_24092370
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html

2015年1月1日木曜日

Swiftプログラマ格付けチェック (2015新年スペシャル)

Swiftプログラマ格付けチェック

今回一流のSwiftプログラマの皆さんに格付けチェックしていただくのはこちら!

JSONライブラリ

です!

ひとつは日本が誇る一流プログラマ、dankogai氏が作成されたJSONライブラリ、githubスター数312
ひとつは当ブログ管理人三流プログラマ、akisuteが適当にググって見つけたJSONライブラリ、スター数16
となっております。
皆様にはこの2つのうちからdankogai氏のライブラリを当てていただきます!

Aのライブラリ
Bのライブラリ
(コメントはすべて削除しています)

それでは正解だと思ったほうの部屋に入っていただきましょう!

Aが正解だと思った人の部屋
Bが正解だと思った人の部屋

2014年12月26日金曜日

Carthage で Code Sign Identity および Provisioning Proifleを直接指定してビルドする方法

誰得情報なのでメモだけ残しておきます。
手元のMacに複数のCode Sign Identityがあってビルドが失敗する人向けです。

akisute Test$ carthage update
*** Fetching SwiftState
*** Fetching SwiftTask
*** Checking out SwiftState at "1.1.1"
*** Checking out SwiftTask at "2.4.0"
*** xcodebuild output can be found in /var/folders/gk/205sh3lx1qdfrwtxcb_tj97m0000gp/T/carthage-xcodebuild.uzBMvq.log
*** Building scheme "SwiftState-iOS" in SwiftState.xcodeproj
*** Building scheme "SwiftState-OSX" in SwiftState.xcodeproj
*** Building scheme "SwiftTask-iOS" in SwiftTask.xcworkspace
*** Building scheme "SwiftTask-OSX" in SwiftTask.xcworkspace
** BUILD FAILED **


The following build commands failed:
Check dependencies

(1 failure)

akisute Test$ tail -n 10 /var/folders/gk/205sh3lx1qdfrwtxcb_tj97m0000gp/T/carthage-xcodebuild.uzBMvq.log
** BUILD SUCCEEDED **

Build settings from command line:
    SDKROOT = macosx10.10

=== BUILD TARGET SwiftState-iOS OF PROJECT SwiftState WITH CONFIGURATION Release ===

Check dependencies

Code Sign error: Multiple matching codesigning identities found: Multiple codesigning identities (i.e. certificate and private key pairs) matching “iPhone Developer” were found.



こういうときは環境変数を使って、
JP11688 kaa$ CODE_SIGN_IDENTITY="iPhone Developer: Masashi Ono" carthage update
*** Fetching SwiftState
*** Fetching SwiftTask
*** Checking out SwiftState at "1.1.1"
*** Checking out SwiftTask at "2.4.0"
*** xcodebuild output can be found in /var/folders/gk/205sh3lx1qdfrwtxcb_tj97m0000gp/T/carthage-xcodebuild.5z3CLW.log
*** Building scheme "SwiftState-iOS" in SwiftState.xcodeproj
*** Building scheme "SwiftState-OSX" in SwiftState.xcodeproj
*** Building scheme "SwiftTask-iOS" in SwiftTask.xcworkspace
*** Building scheme "SwiftTask-OSX" in SwiftTask.xcworkspace


または、
JP11688 kaa$ PROVISIONING_PROFILE="XXXX-XXXX-XXXX-XXXX" carthage update
*** Fetching SwiftState
*** Fetching SwiftTask
*** Checking out SwiftState at "1.1.1"
*** Checking out SwiftTask at "2.4.0"
*** xcodebuild output can be found in /var/folders/gk/205sh3lx1qdfrwtxcb_tj97m0000gp/T/carthage-xcodebuild.5z3CLW.log
*** Building scheme "SwiftState-iOS" in SwiftState.xcodeproj
*** Building scheme "SwiftState-OSX" in SwiftState.xcodeproj
*** Building scheme "SwiftTask-iOS" in SwiftTask.xcworkspace
*** Building scheme "SwiftTask-OSX" in SwiftTask.xcworkspace

参考: http://stackoverflow.com/questions/9264727/code-sign-identity-parameter-for-xcodebuild-xcode4
参考: https://github.com/Carthage/Carthage/issues/235

Container View Controllerを作ってみよう

今日は冬休みの工作ということで、iOSのContainer View Controllerを作ってみようと思います。

Container View Controllerとは

一言で言うと、他のUIViewControllerを包含して表示するUIViewControllerのことです。どのように包含して表示するかによって、たとえばUINavigationControllerやUITabBarController、UIPageViewControllerのような実装があります。

iOS 5からはこのContainer View Controllerを自作する事が可能になりましたが、実装が面倒なのと大体の場合においてUIKitが用意しているContainer View Controllerを使うかcocoapodsあたりからそれっぽいライブラリを拾ってくれば解決するためかあまり具体的な実装方法が話題になっていないようです。今回たまたま作る機会があったのでその時の内容をメモしておこうかと思います。

Container View ControllerへのView Controllerの追加

Container View Controllerを作るには、UIViewControllerを継承したクラスを作成して、そこに- (void)addViewController:(BOOL)animatedのような管理対象のView Controllerを追加するメソッドを作ってやればよいです。
ここで、Container View ControllerにView Controllerを追加する上で基本的にやらなくてはならないことは以下の5ステップにわかれます。
  1. addChildViewController:
    • このタイミングでContainer View Controllerに対象のView Controllerが格納されます。ただしViewはまだ表示されません。
  2. didMoveToParentViewController:
    • Container View Controllerに対象のView Controllerが格納されたことを通知します。
  3. beginAppearanceTransition:animated:
    • これからViewが表示されることをContainer View Controllerおよび対象のView Controllerに通知します。viewWillAppearに相当します。
  4. addSubview:
    • 実際にViewを表示します。必要に応じてアニメーションもつけます。
  5. endAppearanceTransition
    • Viewの表示が完了したことをContainer View Controllerおよび対象のView Controllerに通知します。viewDidAppearに相当します。

実際のサンプルコードはこちら。


Container View ControllerからのView Controllerの削除

View Controllerに追加した時に実施したことを逆順に実施してやればOKです。- (void)removeViewController:(BOOL)animatedのようなメソッドを作ってやって、そこに実装を書けばよいでしょう。
サンプルコードにするとこんな感じになります。


shouldAutomaticallyForwardAppearanceMethodsの設定

先に答えだけいうと、何もしないでいいです。このメソッドの存在そのものを忘れて構いません。

iOS 6から追加されたメソッドで、overrideして使用します。このメソッドがYESを返すときは、Container View Controller自身が他のContainer View Controllerに追加されるなどして表示されviewWillAppear/viewDidAppearが呼び出されるタイミングで自動的にchildViewControllersに対してもviewWillAppear/viewDidAppearを呼び出します。NOの場合は自動的に呼び出されないため手動でchildViewControllersの表示状態を管理し、適時beginAppearanceTransition:animated:を呼び出す必要があります。

デフォルトはYESで、基本的にはデフォルトのまま使えば問題ありません。NOを返したいケースは、たとえばContainer View Controllerが画面に表示されてから一瞬遅れてchildViewControllersをアニメーションしながら表示したいなどの要件がある場合に限られるでしょう。

2014年12月23日火曜日

ReactiveCocoa を Swift から使ってみた

FRP(Functional Reactive Programming)なるものが流行っているらしいので、私もたまには流行に乗っかってみることにしました。手始めにReactiveCocoaをSwiftで一日ほど使ってみました。

導入

こちらのブログにまとまっていますので、そちらを参照していただければ良いかと。基本的にはCocoaPodsで一発です。
http://tnakamura.hatenablog.com/entry/2014/11/15/how_to_use_reactivecocoa_in_swift

ドキュメント

基本的にはプロジェクトのGitHubにしっかりドキュメントがあるのでそれを見ればだいたい大丈夫かなと思います。
https://github.com/ReactiveCocoa/ReactiveCocoa
https://github.com/ReactiveCocoa/ReactiveCocoa/tree/master/Documentation
APIの使い方がわからなければヘッダファイルを見れば相当詳細にコメントがついているのでそれでほぼ問題無いです。それでもわからなければ結構ググればかなりヒットします。熱狂的なファンがいるようです。

概念

こちらのページに非常に詳細に書いてあるのでお勧めです。こちらを見るだけでなんとなく概念がつかめてReactiveCocoaは何をするフレームワークなのかがわかって良いと思います。以下主要なクラスに対して私が理解した内容です(間違ってたらゴメンナサイ)。

RACStream

連続した値を表すすべての既定概念。連続した値というのは今現在すぐに返せる値も通信や計算などによって将来的に返される値も含む。関数型言語で言うところのモナドらしいけどモナドはさっぱり。

RACSequence

RACStreamの実装の一つで、Pull-Baseなもの。すなわちユーザが値を要求して、それに対して値を返すオブジェクト。関数型言語でいうところのリストとかシーケンス。

RACSignal

RACStreamの実装の一つで、Push-Baseなもの。すなわちシステムが何らかのイベントやタイミングに応じて値を返すオブジェクト。もちろん値が返ってくるのは直後かもしれないし遠い未来かもしれない。

RACSubscription

RACSignalに対するコールバックみたいなもの。Promiseパターンのthenとかcatchとかfinallyみたいなもの。

RACCommand

UIBarButtonItemとかUIButtonなどのユーザーインタラクションを表すクラスみたいです。まぁ要するにIBActionみたいなもののようです。加えてRACSignalを使ってenabledの状態をコントロールしたりsenderをfilter/map/reduce/その他いろいろ加工可能。

RACTuple

引数とか返り値とかでよく使われるタプル。Swiftのtupleとは違うので注意。

だいたいこれぐらいわかっていたらコードが書けました。

実験

単純なメモアプリを作って実験しようと思いとりあえずこんなテーブルビューを作ってみました。

そしたら問題が出るわ出るわ。

マクロが使えない

Swiftからだと便利なマクロが使えないので非常に困ります。さらに次に挙げる一部の問題はマクロがないと解決できません。

配列の内容変化に対してRACSignalを取れない

オブジェクトの変化を監視するRACSignalがKVOを元に実装されていて、KVOがArrayに対して使用できないので当然なんですが、これのせいでいきなりReactiveCocoaの世界からいつものCocoaの世界に引き戻されます。
良い対処法はないようです。一応Objective-Cならマクロとか使って対応可能なように見えますがSwiftではどうしようもありませんでした。
https://github.com/ReactiveCocoa/ReactiveCocoa/issues/500
https://github.com/ReactiveCocoa/ReactiveCocoa/issues/1197

delegateパターンをRACSignalに変換するのが厄介

RACSubjectという自分で自由自在に状態を操作できるRACSignalのサブクラスを使ってシグナルをコントロールする方法がまずお手軽です。

またはこちらのブログで紹介されているrac_signalsForSelectorなどを使う方法もあります。
http://spin.atomicobject.com/2014/02/03/objective-c-delegate-pattern/

内部的に黒魔法が多い

先ほどのrac_signalsForSelectorもそうですが、内部で平然とmethod swizzlingを使い放題使いまくっていたりするのでちょっと怖いです。


まとめ

Objective-CでFRPの勉強をするにはちょうどいいんじゃないでしょうか。

2014年12月19日金曜日

SwiftからCやObjective-Cのライブラリを扱うときのテクニック数点

Objective-C Bridging Headerを利用することで、Swiftは既存のいかなるC/Objective-Cコードのシンボルでも呼び出すことが可能になっています。しかしながら場合によってはSwift単体では素直に書きづらいハマりどころがあります。C/Objective-Cのラッパーを作り、Objective-C Bridging Header経由でSwiftから呼び出せば全ての問題は解決できるのですが、面倒くさいですしやはりSwift単体で何とかしたいですよね。そこでここでは素直に書きづらいハマりどころと、それを何とかしてSwift単体で解決する方法をご紹介します。

※以下の情報は2014/12/19現在のものです。Swiftは言語仕様の変化が激しいので予期せず変更されている場合があります、ご了承ください。

1. ARCに管理されていないObjective-Cオブジェクトを扱う

例えばKeychainを扱うAPIなどで、ARCに管理されていないObjective-Cオブジェクトを扱うことがあります。このような場合にはUnmanaged型を使用します。

Unmanaged型はメソッド経由でretain, release, autoreleaseを行うことができるほか、takeUnretainedValue()またはtakeRetainedValue()メソッド経由でT型のSwiftオブジェクトを取り出すことができます。

2. C言語のポインタを扱う

生のC言語のポインタを扱う場合、たいていのケースではUnsafePointer型を使うようにCのAPIがSwiftのAPIに変換されます。このとき、Swift上ではUnsafePointer型を要求するのに、C言語のAPI的にはNULLを渡したい場合には、nilを渡すことができないので、代わりにUnsafePointer.null()を渡すことができます。

UnsafePointer以外にもCOpaquePointer型やCFunctionPointer型に変換されるAPIもありますが、この場合も同様にCOpaquePointer.null()やCFunctionPointer.null()をAPIに渡してやればうまくいきます。

3. cStringUsingEncoding()メソッドの罠に注意する

SwiftにはSwiftネイティブのString型とCocoaのNSString型が存在します。基本的にはこの2つは自動的にうまい具合にブリッジされるためプログラマは違いを意識する必要がありませんが、実はcStringUsingEncoding()メソッドを使う場合にはこれが重大な問題になってきます。String.cStringUsingEncoding()は[CChar]?を、NSString.cStringUsingEncoding()はUnsafePointerを返すのです。さらにコンパイラは文字列をStringとして解釈するのを優先するため、先ほど述べたC言語のAPIに渡す際に型が合わないという理由でエラーになりがちです。

対策として上記の通り明示的にNSStringにキャストすることをおすすめします。

4. enum値のOR結合を何とかする

すみません、なんともなりませんでした(´・_・`)
例えば以下のようなコードを書くこと自体は可能なのですが、適切なenum値を得ることができません。optionsはnilになってしまいます。

どうしてもOR結合が必要なenum値が存在する場合は、現状C/Objective-Cでラッパーを作りObjective-C Bridging Header経由でSwiftから呼び出すしかないようです。