前回の記事から引き続き、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月15日木曜日
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があってビルドが失敗する人向けです。
こういうときは環境変数を使って、
または、
参考: http://stackoverflow.com/questions/9264727/code-sign-identity-parameter-for-xcodebuild-xcode4
参考: https://github.com/Carthage/Carthage/issues/235
手元の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.
Check dependencies
(1 failure)
akisute Test$ tail -n 10 /var/folders/gk/205sh3lx1qdfrwtxcb_tj97m0000gp/T/carthage-xcodebuild.uzBMvq.log
** BUILD SUCCEEDED **
SDKROOT = macosx10.10
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を作ってみようと思います。
iOS 5からはこのContainer View Controllerを自作する事が可能になりましたが、実装が面倒なのと大体の場合においてUIKitが用意しているContainer View Controllerを使うかcocoapodsあたりからそれっぽいライブラリを拾ってくれば解決するためかあまり具体的な実装方法が話題になっていないようです。今回たまたま作る機会があったのでその時の内容をメモしておこうかと思います。
ここで、Container View ControllerにView Controllerを追加する上で基本的にやらなくてはならないことは以下の5ステップにわかれます。
実際のサンプルコードはこちら。
サンプルコードにするとこんな感じになります。
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をアニメーションしながら表示したいなどの要件がある場合に限られるでしょう。
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ステップにわかれます。
- addChildViewController:
- このタイミングでContainer View Controllerに対象のView Controllerが格納されます。ただしViewはまだ表示されません。
- didMoveToParentViewController:
- Container View Controllerに対象のView Controllerが格納されたことを通知します。
- beginAppearanceTransition:animated:
- これからViewが表示されることをContainer View Controllerおよび対象のView Controllerに通知します。viewWillAppearに相当します。
- addSubview:
- 実際にViewを表示します。必要に応じてアニメーションもつけます。
- 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で一日ほど使ってみました。
http://tnakamura.hatenablog.com/entry/2014/11/15/how_to_use_reactivecocoa_in_swift
https://github.com/ReactiveCocoa/ReactiveCocoa
https://github.com/ReactiveCocoa/ReactiveCocoa/tree/master/Documentation
APIの使い方がわからなければヘッダファイルを見れば相当詳細にコメントがついているのでそれでほぼ問題無いです。それでもわからなければ結構ググればかなりヒットします。熱狂的なファンがいるようです。
だいたいこれぐらいわかっていたらコードが書けました。
そしたら問題が出るわ出るわ。
良い対処法はないようです。一応Objective-Cならマクロとか使って対応可能なように見えますがSwiftではどうしようもありませんでした。
https://github.com/ReactiveCocoa/ReactiveCocoa/issues/500
https://github.com/ReactiveCocoa/ReactiveCocoa/issues/1197
またはこちらのブログで紹介されているrac_signalsForSelectorなどを使う方法もあります。
http://spin.atomicobject.com/2014/02/03/objective-c-delegate-pattern/
導入
こちらのブログにまとまっていますので、そちらを参照していただければ良いかと。基本的には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から呼び出すしかないようです。
2014年9月30日火曜日
GitHub:Enterprise 環境下では CocoaPods を 0.34.0 以上にアップデートしてはいけません
タイトルの地点で結構出落ちですが、本日少々厄介な問題にぶち当たってしまったので共有いたします。
発生条件
CocoaPods 0.34.0以上を使用し、GitHub:EnterpriseのgitリポジトリをPodSpecないしSourceリポジトリとして使用している場合に発生します。
発生する問題
CocoaPodsのgitを利用するアクションが全く成功しなくなります。具体的にはpod install, pod updateなどが全く通りません。
問題の原因
公式ブログにあるとおり、0.34.0以上からCocoaPodsは高速にgitリポジトリをcloneするため--depth 1 (shallow clone) オプションを採用するようになっています。しかしながらGitHub:Enterprise側の技術的問題なのか、GitHub:Enterprise内のgitリポジトリに対するhttpないしhttps経由でのshallow cloneが成功しないため永遠にpod installやpod updateが終わりません。
対処方法
- CocoaPodsを当面の間0.33.1のまま運用する。0.33.1を指定してインストールするときにはこちらのQiita記事が参考になります。
- http/httpsではなくssh clone URLを使用する。sshだと問題が起きないとの報告があります。GitHubに問い合わせてみたところやはりhttp/httpsのみで問題が発生するようです。
- ssh cloneがネットワーク上の問題などで使用できない場合、最後の回避策として:tagや:branchを使用しないで:commitを直指定するように社内のPodspecを書き直す。
根本的にはCocoaPodsかGitHub:Enterpriseかいずれかが修正されるのを待つしかありませんが、CocoaPods側としてはあまり積極的に対応する気がないようなので、GitHub:Enterprise側に対応を期待するしかないでしょうか。もちろん全てのGitHub:Enterprise環境下で発生するとは限りませんが、いずれにせよ注意する必要があります。
2014/10/01追記
GitHub:Enterpriseに問い合わせてみたところ、こちらは既知の問題であり現在修正中で、次のリリース時に修正されるそうです。次のリリースが社内環境にデプロイされるまでの辛抱ですね。
こちらからは以上です。
登録:
投稿 (Atom)
