tag:blogger.com,1999:blog-51918093322572642172024-02-07T13:12:27.861+09:00Safxプログラミング (iOS, JavaScript, Jenkins, Sikuli) とMacやiPhoneなどの話題が中心のブログAnonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.comBlogger243125tag:blogger.com,1999:blog-5191809332257264217.post-62521816341174404662016-07-12T10:00:00.000+09:002016-07-12T10:00:04.012+09:00Xcode 8のソースコードエクステンションで「突然の死」をつくってみた<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgT4KT3uNJPAoJv6w1g1ozH5tYqPUlxstgfAru2lPTWVRWSqyf81K5whF-JyCiU7-nusFi6_DH8ifdBMQm_ThzEm0oP1xB44Q5VZvdnwl3zh9UA8MPSHDJcrcmP2_zq-eKolQIFNKMB6mE/s1600/Totsuzen_no_hoge.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgT4KT3uNJPAoJv6w1g1ozH5tYqPUlxstgfAru2lPTWVRWSqyf81K5whF-JyCiU7-nusFi6_DH8ifdBMQm_ThzEm0oP1xB44Q5VZvdnwl3zh9UA8MPSHDJcrcmP2_zq-eKolQIFNKMB6mE/s277/Totsuzen_no_hoge.png" width="277" height="155" /></a></div>
<p>Xcode 8からXcode Source Code Extensionという機能が追加されて、
公式の機能として自作アプリからXcodeのソースコードをいじることができるようになりました。</p>
<p>そこで、サンプルとして「<a href="https://itunes.apple.com/jp/app/tu-ranno-si/id737765108?mt=8">突然の死</a>」エクステンションをつくってみました、という関する内容で<a href="http://cocoa-kansai.connpass.com/event/33515/">第68回 Cocoa勉強会関西</a>で発表しましたが、諸事情でスクリーンショットが載せられませんので、スライドはアップロードしていません。</p>
<p>その代わりではありませんが、そのソースコードをGitHub上で公開しています。</p>
<ul>
<li><a href="https://github.com/safx/XcodeExtension-TotsuzenNoShi">GitHub - safx/XcodeExtension-TotsuzenNoShi</a></li>
</ul>
<p>(上のスクリーンショットはXcode 8 betaでの実行結果をXcode 7で開いているものです)</p>
<a name='more'></a>
<h2 id="">概要</h2>
<p>使いかたは、AppExtensionを起動すると闇のXcode (アイコンが黒い) が起動するので、そこから別プロジェクトを開いてください。
文字列を選択した上で、メニュー→Editorあたりに拡張のメニューが生えているのでこれを実行させてください。</p>
<p>ソースコードを見ればわかりますが、1行以上選択したらエラーにしていたり、フォントによっては大きくずれる可能性があります。
また、上のスクリーンショットを見ればわかりますが、そもそも実用性はありません。</p>
<h2 id="">実行時の注意点</h2>
<p>Qiitaの記事「<a href="http://qiita.com/_tid_/items/271c1973b3043f6c55cb">Xcode Source Editor Extensionを試す</a>」に書かれている次の2点には注意してください。
特に2点めは、Debug Navigatorタブが情報を表示されてからさらに数秒くらい待つくらいでちょうどよいくらいです。</p>
<ul>
<li>macOS 10.11では<code>sudo /usr/libexec/xpccachectl</code>をして再起動しておく必要がある</li>
<li>拡張が実行状態になるまでにかなり時間がかかります。その前に黒いXcodeでプロジェクトを起動するとうまく動かなくなります (闇のXcodeを再起動させる必要あり)</li>
</ul>
<h2 id="xcodesourcecodeextension">Xcode Source Code Extension</h2>
<p>Xcode Source Code Extensionのつくりかた自体は次のWWDC 2016のムービー内のデモを見れば大まかな流れはわかるでしょう。</p>
<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2016/414/">Using and Extending the Xcode Source Editor - WWDC 2016 - Videos - Apple Developer</a></li>
</ul>
<p>基本的には<code>XCSourceEditorCommand</code>の<code>perform(with:completionHandler:)</code>を実装すればよいです。</p>
<p><code>XCSourceEditorCommandInvocation</code>の<code>buffer</code>内から、</p>
<ul>
<li>ソースコード文字列</li>
<li>文字列の選択範囲</li>
<li>ファイル種類 (UTI)</li>
<li>タブ幅</li>
<li>インデント量</li>
</ul>
<p>などを取ることができます。</p>
<p>ソースコード文字列は、</p>
<ul>
<li>行ごとの文字列の配列</li>
<li>ソースコード全体の文字列</li>
</ul>
<p>の両方があって、片方をいじるともう一方も変わるようになっています。
これがMutableな配列や文字列になっているので、これをいじって最後に<code>completionHander(nil)</code>すればXcodeのソースコードが変更されます。</p>
<h2 id="">その他</h2>
<p>エクステンションでの処理に時間がかかっても、「The command “突然の死” is still busy…」みたいな情報バーが出てきますが、別プロセスなのでその間もXcodeを操作することはできました。</p>
<h2 id="">おわりに</h2>
<p>Xcode 8のソースコードエクステンションについて紹介しました。</p>
<p>現在のソースコードの文字列しか得られないので使い勝手はよくない印象ですが、
対応アプリをAppStoreに出すこともできるので、対応する価値はあるかもしれません。</p>
<h2 id="">関連リンク</h2>
<ul>
<li><a href="https://developer.apple.com/videos/play/wwdc2016/414/">Using and Extending the Xcode Source Editor - WWDC 2016 - Videos - Apple Developer</a></li>
<li><a href="http://qiita.com/_tid_/items/271c1973b3043f6c55cb">Xcode Source Editor Extensionを試す - Qiita</a></li>
<li><a href="http://cocoa-kansai.connpass.com/event/33515/">第68回 Cocoa勉強会関西 - connpass</a></li>
<li><a href="https://itunes.apple.com/jp/app/tu-ranno-si/id737765108?mt=8">App Store: 突然の死</a></li>
</ul>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-39407157489767253592016-04-27T10:00:00.000+09:002016-04-27T10:00:18.079+09:00関西モバイルアプリ研究会 #13 で React Native について発表しました<div class="separator" style="clear: both; text-align: center;"><a href="https://4.bp.blogspot.com/-bpNMGU1MIJ4/Vx-GsTSVuZI/AAAAAAAADmY/3Ese5qdIgQQTZAWPcIamTFlf9H-8OwrJQCLcB/s1600/Yahoo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://4.bp.blogspot.com/-bpNMGU1MIJ4/Vx-GsTSVuZI/AAAAAAAADmY/3Ese5qdIgQQTZAWPcIamTFlf9H-8OwrJQCLcB/s400/Yahoo.jpg" /></a></div>
<p>今回はYahoo! JAPAN 大阪での開催でした。</p>
<a name='more'></a>
<script async class="speakerdeck-embed" data-id="af95a950eaef4d7599f256111433d0f7" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"></script>
<p>5分なのにライブコーディングしてしまいましたが、どうにかなりました。</p>
<p>スライドではgif画像がアニメーションしないので、下に貼りつけておきます。</p>
<p>Live Reloadの紹介。
<div class="separator" style="clear: both; text-align: center;"><a href="https://3.bp.blogspot.com/-ToklVAGr8vo/Vx-MyuMs_BI/AAAAAAAADmo/zjP6TxVCscAJGaPTiM0S2tBvT8RCjitmgCLcB/s1600/ReactNative1.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://3.bp.blogspot.com/-ToklVAGr8vo/Vx-MyuMs_BI/AAAAAAAADmo/zjP6TxVCscAJGaPTiM0S2tBvT8RCjitmgCLcB/s400/ReactNative1.gif" /></a></div></p>
<p>クリックカウンタを作ってみるところ。
<div class="separator" style="clear: both; text-align: center;"><a href="https://2.bp.blogspot.com/-hBiNHfgXM-w/Vx-MyrQlWxI/AAAAAAAADms/_6bHjXAocboWNaKUJGq_GAWW_HDoXx2vQCLcB/s1600/ReactNative2.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://2.bp.blogspot.com/-hBiNHfgXM-w/Vx-MyrQlWxI/AAAAAAAADms/_6bHjXAocboWNaKUJGq_GAWW_HDoXx2vQCLcB/s400/ReactNative2.gif" /></a></div></p>
<p>時間が足りなくなると思っていろいろ説明を飛ばしたり、
後の質問なんかでいろいろ自分の回答に間違いがあったりしたので、いろいろ補足させていただきます。</p>
<h3 id="jsx">JSXについて</h3>
<p>上のgif画像上で編集している<code>index.ios.js</code>のコードは<a href="https://facebook.github.io/react/docs/jsx-in-depth.html">JSX</a>という、Facebookが提案しているJSの拡張言語です。</p>
<p>内部的には、<a href="https://babeljs.io/">Babel</a> + <a href="https://babeljs.io/docs/learn-es2015/">ES2015</a>プラグイン + JSXプラグインを利用しているようです。</p>
<h3 id="">ネットワークまわりセキュリティまわりどうなの?</h3>
<p>開発ではローカルサーバを使う状態になっていて、このときのATSの設定は「Allow Arbitrary Loads」な状態です。</p>
<p>ただし、<a href="https://facebook.github.io/react-native/docs/running-on-device-ios.html#using-offline-bundle">Using offline bundle</a>
あたりによると、ファイルバンドルを用いるようにビルドすればオフラインで利用できるようになるみたいです。</p>
<p>なので、ATSの設定を消して、オフラインバンドルを利用すると、質問にあったような問題はなくなるかと思います。</p>
<p>オフラインバンドルを利用するには、AppDelegate.mが次のようになっているので、</p>
<pre class="brush:objc">- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *jsCodeLocation;
/**
* Loading JavaScript code - uncomment the one you want.
*
* OPTION 1
* Load from development server. Start the server from the repository root:
*
* $ npm start
*
* To run on device, change `localhost` to the IP address of your computer
* (you can get this by typing `ifconfig` into the terminal and selecting the
* `inet` value under `en0:`) and make sure your computer and iOS device are
* on the same Wi-Fi network.
*/
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
/**
* OPTION 2
* Load from pre-bundled file on disk. The static bundle is automatically
* generated by the "Bundle React Native code and images" build step when
* running the project on an actual device or running the project on the
* simulator in the "Release" build configuration.
*/
//jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"MyFirstProject"
initialProperties:nil
launchOptions:launchOptions];
:
</pre>
<p>上記のOption 2のほうを選択して (コメントを外して)、リリースビルドすればいいみたいです。</p>
<h3 id="ui">ネイティブUIを簡単に使えるようになるの?</h3>
<p>Objective-Cでビューをつくる場合だと、</p>
<ol>
<li>(ObjC) <code>RCTViewManager</code>を継承する</li>
<li>(ObjC) <code>RCT_EXPORT_MODULE()</code>を足す</li>
<li>(ObjC) <code>- view</code>メソッドを実装する</li>
<li>(JS) Nativeコードを呼ぶためのJSのコードを書く</li>
</ol>
<p>と利用できるようになるようです。</p>
<p>Objective-Cのコードは、次のような感じ。</p>
<pre class="brush:objc">@interface RCTMapManager : RCTViewManager
@end
@implementation RCTMapManager
RCT_EXPORT_MODULE()
- (UIView *)view
{
return [[MKMapView alloc] init];
}
@end
</pre>
<p>JSのコードは、次のような感じになります。</p>
<pre class="brush:js">import { requireNativeComponent } from 'react-native';
module.exports = requireNativeComponent('RCTMap', null);
</pre>
<p>実際には、プロパティ、イベント、スタイルなんかもきちんと定義してやる必要があります。
詳細は次のページを参照ください。</p>
<ul>
<li><a href="https://facebook.github.io/react-native/docs/native-components-ios.html#content">Native UI Components – React Native | A framework for building native apps using React</a></li>
</ul>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTgyHVZvGXFZs6lhUZhyphenhyphenZ-33mxxtj0m4ezVM8UZGnmL0GFkqoI5G1jeZ_UOt5eYJa4M2l0JGKfHZ1EPHl2FXiOsEbx23umGmr5ELqcyDGDEF903nIiHfKBV1iSICDJKcWv0QOuxjGgJfI/s1600/Screen+Shot+2016-04-19+at+0.43.00.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTgyHVZvGXFZs6lhUZhyphenhyphenZ-33mxxtj0m4ezVM8UZGnmL0GFkqoI5G1jeZ_UOt5eYJa4M2l0JGKfHZ1EPHl2FXiOsEbx23umGmr5ELqcyDGDEF903nIiHfKBV1iSICDJKcWv0QOuxjGgJfI/s400/Screen+Shot+2016-04-19+at+0.43.00.png" /></a></div>
<h3 id="js">JSのかわりに他の言語は使えるの?</h3>
<p>JSに変換できる言語なら、(無理やり使うのでよければ) だいたいの言語は利用できるような気がします。</p>
<p>TypeScriptはJSXをサポートしていて、<a href="https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/react-native">react-nativeの型定義</a>みたいなのもあるみたいですし、ちゃんと使える気がします。</p>
<h3 id="">制限事項は?</h3>
<p><a href="https://facebook.github.io/react-native/docs/known-issues.html#content">Known Issues</a>を参照ください。</p>
<p>とりあえず、今のところAndroid Mのパーミッションモデルはサポートしていないなどの問題があります。</p>
<h3 id="">将来性は?</h3>
<p>似た感じの廃れた技術がいっぱいあるので、どうなるのかなんとも言えません。</p>
<p>Reactを知っているJS使いにとっての敷居は低そうなので、個人的にはそこそこ流行りそうな気はします。</p>
<h3 id="">データを頻繁に変える状況でいいよね、的な話</h3>
<p>「カンファレンスで使える!」みたいな話はParse Serverあたりの話で、React Nativeとはまったく関係ありませんでした。すみません。</p>
<ul>
<li><a href="http://makeitopen.com/tutorials/building-the-f8-app/planning/">Planning The App — Makeitopen.com - Open Source Learning</a></li>
</ul>
<h2 id="">おわりに</h2>
<p>会場と食事の提供をくださったヤフー株式会社に感謝いたします。</p>
<h2 id="">関連リンク</h2>
<ul>
<li><a href="https://facebook.github.io/react-native/">React Native | A framework for building native apps using React</a></li>
<li><a href="https://babeljs.io/">Babel · The compiler for writing next generation JavaScript</a></li>
<li><a href="http://facebook.github.io/react/">A JavaScript library for building user interfaces | React</a></li>
<li><a href="https://www.typescriptlang.org/">TypeScript - JavaScript that scales.</a></li>
</ul>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-27896528609630318342016-03-11T10:00:00.000+09:002016-03-11T10:00:15.953+09:00Swift 2.1のジェネリクスでできないことまとめ
<div class="separator" style="clear: both; text-align: center;"><a href="https://2.bp.blogspot.com/--GANr7_ytDE/VuGXykwFWCI/AAAAAAAADmE/14nETgWyM0c/s1600/P2121438.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://2.bp.blogspot.com/--GANr7_ytDE/VuGXykwFWCI/AAAAAAAADmE/14nETgWyM0c/s400/P2121438.jpg" /></a></div>
<p>個人的に開発中によく遭遇したジェネリクスでのエラーあたりについてまとめてみました。</p>
<p>なお、Swiftコンパイラテストによる、実際にコンパイルできる/できない例は次のテストケースを参照してください。</p>
<ul>
<li>https://github.com/apple/swift/blob/a048b078e37dfafc0e188bb8c6f3f50f5f796494/test/decl/ext/generic.swift</li>
</ul>
<a name='more'></a>
<h2 id="tldr">TL;DR</h2>
<ul>
<li>ジェネリックパラメータは制限がきびしい
<ul>
<li>制約では<code>==</code>は使えない</li>
<li>準拠要求には非プロトコル型は使えない</li>
</ul></li>
<li>要求節と継承節を同時につけられない</li>
<li><code>Self</code>はプロトコル拡張でしか使えない</li>
<li>内部クラスをジェネリッククラスにしたり、ジェネリッククラスに内部クラスを持たせたりできない</li>
</ul>
<h2 id="">用語</h2>
<p>まずはエラーメッセージ内でよく出てくる単語をまとめてみます。</p>
<ul>
<li><code>requirement-clause</code>: (要求節) <code>where</code>節以下の定義のこと</li>
<li><code>constraints</code>: (制約) エラーメッセージ中では、<code>requirement-clause</code>をこう呼ぶこともあるようだ</li>
<li><code>same-type requirement</code>: (同値要求) <code>==</code>を使った制約のこと。<code>==</code>の両辺には型パラメータ、プロトコル型、クラス型、構造体型などが使える。</li>
<li><code>conformance requirement</code>: (準拠要求) <code>:</code>を使った制約のこと。左辺には型パラメータ、右辺にはプロトコル型とクラス型のみ許される (型パラメータと構造体型は不可)。</li>
<li><code>inheritance clause</code>: (継承節) 拡張の<code>protocol</code>継承のこと。</li>
<li><code>associated type</code>: (連想型) プロトコル定義中の<code>=</code>代入のない<code>typealias</code>のこと。Swift 3からは<code>associatedtype</code>となる予定</li>
<li><code>generic parameter</code>: (ジェネリックパラメータ) ジェネリクスのパラメータのこと</li>
</ul>
<p>実際のクラスなどに当てはめてみると次のようになります。</p>
<pre class="brush:swift">extension Collection
: SomeProtocol // inheritance clause
where // ↓ ここから requirement clause (constraints)
Element: Comparable, // conformance requirement
Element == Int // same-type requirement
// ここまで requirement clause (constraints)
{ }
Protocol SomeProtocol {
typealias SomeType // associated type
}
struct SomeStruct
<Element> // generic parameter
{ }
struct OtherStruct
<
Element: Compatible // conformance requirement
where // ↓ ここから requirement clause (constraints)
Element: Equatable // conformance requirement
> // ここまで requirement clause (constraints)
{ }
</pre>
<h2 id="">できないこと</h2>
<h3 id="">ジェネリックパラメータに<code>==</code>を使うこと</h3>
<p>下の2つの項より、実質的にジェネリクスのパラメータに<code>==</code>を使える状況がないと思われます。</p>
<h3 id="">ジェネリックパラメータにを非ジェネリックにしてしまうこと</h3>
<p>ジェネリックパラメータは、<code>==</code>を使った制約でジェネリックでない (non-generic) ようにすることはできません。</p>
<pre class="brush:swift">// error: same-type requirement makes generic parameter 'Element' non-generic
extension Array where Element == Int { }
</pre>
<p>プロトコルも非ジェネリックと扱われるので、同じエラーになります。</p>
<pre class="brush:swift">// error: same-type requirement makes generic parameter 'Element' non-generic
extension Array where Element == Comparable { }
</pre>
<p>なお、ジェネリックパラメータには継承節が使えるので、それを利用して書きましょう。上の例は次のように書けばOKです。</p>
<pre class="brush:swift">extension Array where Element: Comparable { }
</pre>
<p>また、プロトコルの<code>typealias</code>は当然ながらジェネリックパラメータではありません。
そのため、プロトコル拡張などでは<code>==</code>を使えます。</p>
<pre class="brush:swift">extension CollectionType where Index == UInt64 { }
extension GeneratorType where Element == Hashable { }
</pre>
<h3 id="">ジェネリックパラメータを同値関係にしてしまうこと</h3>
<p>ジェネリクスのパラメータは、他のジェネリックパラメータとの同値関係に対して使うこともできません。</p>
<pre class="brush:swift">struct Foo<T, U> {
let t: T
let u: U
}
// error: same-type requirement makes generic parameters 'T' and 'U' equivalent
extension Foo where T == U { }
</pre>
<p>もちろん、プロトコルの<code>typealias</code>ならOK。</p>
<pre class="brush:swift">protocol SomeProtocol {
typealias A
typealias B
}
extension SomeProtocol where A == B { }
</pre>
<h3 id="">ジェネリックパラメータに非プロトコル型の準拠要求をつけること</h3>
<p>継承節での継承で、プロトコル型とクラス型以外のものを指定するとエラーになります。</p>
<pre class="brush:swift">// error: type 'Element' constrained to non-protocol type 'Int'
extension Array where Element: Int { }
// error: type 'Element' constrained to non-protocol type 'String'
extension Array where Element: String { }
</pre>
<p>プロトコル型とクラス型の継承は付けることができます。</p>
<pre class="brush:swift">extension Array where Element: Hashable { }
extension Array where Element: NSObject { }
</pre>
<p>なお、ジェネリックパラメータも非プロトコル型の扱いでエラーになります。</p>
<pre class="brush:swift">struct Foo<T, U> {
let t: T
let u: U
}
// error: type 'T' constrained to non-protocol type 'U'
extension Foo where T: U { }
</pre>
<h3 id="">制約つきの拡張にプロトコル型の継承をつけること</h3>
<p>制約と継承を同時につけることはできません。</p>
<pre class="brush:swift">// error: extension of type 'Array' with constraints cannot have an inheritance clause
extension Array : SomeProtocol where Element: Comparable { }
</pre>
<p>これはプロトコル拡張の場合でも同様です。</p>
<pre class="brush:swift">protocol SomeProtocol {}
// error: extension of protocol 'GeneratorType' cannot have an inheritance clause
extension GeneratorType: SomeProtocol where Element: Comparable { }
</pre>
<p>ただし、プロトコル拡張だと<code>Self</code>が使えるので、継承を使わずに次のように書くことができます。</p>
<pre class="brush:swift">extension GeneratorType where Self: SomeProtocol, Element: Comparable { }
</pre>
<h2 id="self">プロトコル以外の拡張で<code>Self</code>を使うこと</h2>
<p>プロトコル及びメソッドの戻り値に対してのみ、<code>Self</code>を使うことができます。それ以外ではエラーになります。</p>
<pre class="brush:swift">// error: 'Self' is only available in a protocol or as the result of a method in a class; did you mean 'Array'?
extension Array where Self: SomeProtocol, Element: Comparable { }
</pre>
<p>ちなみに、QuickFixに従って次のように修正してもエラーになります。<code>Array</code>内に<code>Array</code>というジェネリックパラメータや連想型がないからです。</p>
<pre class="brush:swift">// error: type 'Array' in conformance requirement does not refer to a generic parameter or associated type
extension Array where Array: SomeProtocol, Element: Comparable { }
</pre>
<p>この場合には参照できるようなプロトコルがあればそれを使うくらいしか手がないでしょう。</p>
<pre class="brush:swift">extension _ArrayType where Self: SomeProtocol, Element: Comparable { }
</pre>
<p>どうしてもクラスを制約したければ、妥協するしかありません。</p>
<pre class="brush:swift">extension Array: SomeProtocol {}
</pre>
<h2 id="">内部クラスをジェネリッククラスにすること</h2>
<p>内部クラスをジェネリッククラスにすることはできません。</p>
<pre class="brush:swift">// error: generic type 'SS' nested in type 'S' is not allowed
struct S {
struct SS<T> {}
}
</pre>
<p>ジェネリッククラスを外に出してあげましょう。</p>
<pre class="brush:swift">struct S {}
struct SS<T> {}
</pre>
<h2 id="">ジェネリッククラスに内部クラスをつくること</h2>
<p>ジェネリッククラスに内部クラスをつくることもできません。</p>
<pre class="brush:swift">// error: type 'SS' nested in generic type 'S' is not allowed
struct S<T> {
struct SS {}
}
</pre>
<p>こちらも、ジェネリッククラスを外に出してあげましょう。</p>
<pre class="brush:swift">struct S<T> {}
struct SS {}
</pre>
<h2 id="">拡張に<code><></code>を使うこと</h2>
<p>C++なんかを使っている人だとジェネリクスパラメータを<code>Array<Int></code>みたいに特殊化したくなりますが、これはエラーになります。</p>
<pre class="brush:swift">// error: constrained extension must be declared on the unspecialized generic type 'S' with constraints specified by a 'where' clause
struct S<T> {}
extension S<Int> {}
</pre>
<p>指示通りに<code>where</code>を使ったrequirement節に書きかえてください。ただし、<code>Int</code>はプロトコル型でもクラス型でもありませんのでエラーになります。</p>
<pre class="brush:swift">// error: type 'T' constrained to non-protocol type 'Int'
extension S where T: Int {}
</pre>
<p>少し違いますが、<code>IntegerType</code>を使うならOK。</p>
<pre class="brush:swift">extension S where T: IntegerType {}
</pre>
<h2 id="">プロトコルで連想型での要求に自分自身を用いること</h2>
<p>プロトコル内の連想型での要求部分に自分自身を用いることはできません。</p>
<pre class="brush:swift">// error: type may not reference itself as a requirement
protocol P {
typealias S: P
}
</pre>
<h2 id="swift3.0">いろいろな制限はSwift 3.0以降で緩和されていくかも</h2>
<p>今月にSwift-Evolutionメーリングリスト内で、AppleのSwiftのリードエンジニアであるDouglas Gregor氏による<a href="https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160229/011666.html">Manifesto: Completing Generics</a>という投稿がありました。</p>
<ul>
<li><a href="https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160229/011666.html">swift-evolution: Manifesto: Completing Generics</a></li>
</ul>
<p>これはジェネリクスに対する改善提案です。
上の問題のいくつかを解決するような提案もなされています。</p>
<p>提案段階で、実現するかどうかもわかっていない (早くてもSwift 3.0以降らしい) ですが、この中からおもしろそうなものをいくつかピックアップして紹介すると、</p>
<ul>
<li><p>プロトコル内にデフォルト実装を書けるように (わざわざ拡張で書く必要がなくなる!)</p></li>
<li><p><code>protocol<…></code>を<code>Any<…></code>に変更</p></li>
<li><p>文法を見やすく (<code>where</code>を後ろにもっていく)</p>
<pre class="brush:swift">func containsAll<S: Sequence>(elements: S) -> Bool
where Sequence.Iterator.Element == Element
</pre></li>
<li><p>Generic typealiases</p>
<pre class="brush:swift">typealias StringDictionary<Value> = Dictionary<String, Value>
</pre></li>
<li><p>Variadicなジェネリクス</p>
<pre class="brush:swift">public struct ZipIterator<... Iterators : IteratorProtocol> : Iterator { ... }
</pre></li>
<li><p>パラメータ付き拡張</p>
<pre class="brush:swift">extension<T> Array where Element == T? { ... }
</pre></li>
<li><p>associatedtypeに任意を制約を付けられるように</p>
<pre class="brush:swift">associatedtype SubSequence : Sequence where SubSequence.Iterator.Element == Iterator.Element
</pre></li>
<li><p>ジェネリックパラメータのデフォルト値</p>
<pre class="brush:swift">public final class Promise<Value, Reason=Error> { … }
</pre></li>
</ul>
<p>提案段階なので文法が実際どうなるかはわかりません。
これ以外にもいろいろな改良があるようでこれからのジェネリクスまわりの変化が楽しみですね。</p>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-72779071802673707562016-02-26T10:00:00.000+09:002016-02-26T10:00:25.391+09:00関西モバイルアプリ研究会 #11でSwiftのLensについて発表しました<div class="separator" style="clear: both; text-align: center;"><a href="https://4.bp.blogspot.com/-Wiyz_YkHssA/Vs-XQ-kZZvI/AAAAAAAADl0/13KuPZZlzzo/s1600/Screen%2BShot%2B2016-02-26%2Bat%2B9.07.01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://4.bp.blogspot.com/-Wiyz_YkHssA/Vs-XQ-kZZvI/AAAAAAAADl0/13KuPZZlzzo/s400/Screen%2BShot%2B2016-02-26%2Bat%2B9.07.01.png" /></a></div>
<p><a href="http://kanmoba.connpass.com/event/26645/">関西モバイルアプリ研究会 #11</a>でSwiftのLensについて発表しました。</p>
<p>今回は<a href="http://www.innovation-osaka.jp/ja/">大阪イノベーションハブ</a>で行われ、関モバでは始めての大阪開催となりました。</p>
<a name='more'></a>
<script async class="speakerdeck-embed" data-id="6f821defdbc646999656fb1c20b70bb6" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"></script>
<p>Lens自体については次のQiita記事が参考になります。</p>
<ul>
<li><a href="http://qiita.com/to4iki/items/f0cc28e1102cf53be85d">SwiftでLens(すごいgetter/setter)を実装してみた - Qiita</a></li>
</ul>
<h2 id="lensyswift-idl">Lensy & Swift-IDL</h2>
<p><code>Optional</code>と<code>Array</code>に対応して、Resultを返すLensをつくりました。</p>
<ul>
<li><a href="https://github.com/safx/Lensy">safx/Lensy</a></li>
</ul>
<p><code>Optional</code>と<code>Array</code>にはそれぞれ、<code>OptionalUnwrapLens</code>と<code>ArrayIndexLens</code>を使います。</p>
<pre><code>editor.compose(OptionalUnwrapLens<Person>()).compose(name).get(book) // OK("Sasaki")
authors.compose(ArrayIndexLens<Person>(at: 0)).compose(name).get(book) // OK("Yamada")
authors.compose(ArrayIndexLens<Person>(at: 1)).compose(composed).get(book) // OK("Yokohama")
authors.compose(ArrayIndexLens<Person>(at: 2)).compose(composed).get(book) // Error(ArrayIndexOutOfBounds)
</code></pre>
<p>また、Lensをフィールドの数だけ自分で用意するのはさすがにつらいので、自動生成するツールをつくりました。</p>
<ul>
<li><a href="https://github.com/safx/swift-idl">safx/swift-idl</a></li>
</ul>
<p>swift-idlの自動生成にはHelperが含まれており、これは<code>$</code>でアクセスできます。
Helperを使うと、<code>.</code>記法での記述ができて補完も効くようになります</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsIBLwwh-YQb1InkTlsEn7aTvoYJIws94OmfERJGwId1CH8r6KGueHprT1kUvlKYYosFXvZaDfEOfDIFbrU7zk1utD6o9crMehb50HG4kUkWtErm7rtomOON4gSd_rawaSbIP5WGNiuRA/s1600/lens-helper-completion.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsIBLwwh-YQb1InkTlsEn7aTvoYJIws94OmfERJGwId1CH8r6KGueHprT1kUvlKYYosFXvZaDfEOfDIFbrU7zk1utD6o9crMehb50HG4kUkWtErm7rtomOON4gSd_rawaSbIP5WGNiuRA/s800/lens-helper-completion.gif" /></a></div>
<p>また、<code>[]</code>記法での配列の要素アクセスができるようになります。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://4.bp.blogspot.com/-ozW2SuYEPpQ/Vs-VOUyjdqI/AAAAAAAADls/2CXTdiKhsxQ/s1600/lens-result2.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://4.bp.blogspot.com/-ozW2SuYEPpQ/Vs-VOUyjdqI/AAAAAAAADls/2CXTdiKhsxQ/s800/lens-result2.gif" /></a></div>
<p>ただし、<code>Optional</code>のアンラップには<code>unwrap</code>を利用します。これは、Swiftでは演算子<code>!</code>のオーバライドが禁止されているからです。</p>
<p>その他の利用方法については、それぞれのREADMEを参照してください。</p>
<ul>
<li><a href="https://github.com/safx/Lensy">safx/Lensy</a></li>
<li><a href="https://github.com/safx/swift-idl">safx/swift-idl</a></li>
</ul>
<h2 id="">雑感</h2>
<p>会場の利用時間の都合もあって、発表者の抽選がありました。発表側の抽選が意味をなしたのも、関モバでは始めてのはず。
発表者を含む参加者が50人を超えたのも始めてで、いろいろ始めてづくしの回でした。</p>
<h2 id="">関連リンク</h2>
<ul>
<li><a href="http://kanmoba.connpass.com/event/26645/">関西モバイルアプリ研究会 #11 - connpass</a></li>
<li><a href="http://www.innovation-osaka.jp/ja/">Osaka Innovation Hub – 大阪発、イノベーションのエコシステム | Osaka Innovation Hub</a></li>
<li><a href="https://hackage.haskell.org/package/lens">lens: Lenses, Folds and Traversals | Hackage</a></li>
<li><a href="http://qiita.com/to4iki/items/f0cc28e1102cf53be85d">SwiftでLens(すごいgetter/setter)を実装してみた - Qiita</a></li>
<li><a href="https://github.com/safx/Lensy">safx/Lensy</a></li>
<li><a href="https://github.com/safx/swift-idl">safx/swift-idl</a></li>
</ul>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-81710168996089846662016-02-18T10:00:00.000+09:002016-02-18T10:00:11.013+09:00RxSwift用のObservableなArrayをつくった<div class="separator" style="clear: both; text-align: center;"><a href="https://3.bp.blogspot.com/-PYw_iuQoYRQ/VsSShXLaIMI/AAAAAAAADlU/xhDwe5nurY8/s1600/P8111045.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://3.bp.blogspot.com/-PYw_iuQoYRQ/VsSShXLaIMI/AAAAAAAADlU/xhDwe5nurY8/s400/P8111045.jpg" /></a></div>
<p>Arrayのメソッドを持っていて、保持している要素が変化したときにはメッセージを投げる配列っぽいものをつくりました。</p>
<p>最新のRxSwift 2.2に対応しており、PodとCarthageで利用可能です。</p>
<ul>
<li><a href="https://github.com/safx/ObservableArray-RxSwift">safx/ObservableArray-RxSwift</a></li>
</ul>
<a name='more'></a>
<h2 id="">基本的な使いかた</h2>
<p>こんな感じで宣言します。</p>
<pre class="brush:swift">var array: ObservableArray<String> = ["foo", "bar", "buzz"]
</pre>
<p><code>ObservableArray</code>には、Rxのメッセージ用のメソッド<code>rx_elements()</code>と<code>rx_events()</code>を持っています。</p>
<pre class="brush:swift">func rx_elements() -> Observable<[Element]> // 要素全体
func rx_events() -> Observable<ArrayChangeEvent> // 差分によるイベント
</pre>
<p>通常のRxSwiftのように<code>subscribe</code>して使えます。</p>
<pre class="brush:swift">array.rx_events().subscribeNext { print($0) }
</pre>
<p><code>rx_events</code>は変化した後の配列の要素すべてを投げますので、ここから、次のようにメソッドを呼ぶと、</p>
<pre class="brush:swift">array.append("coffee")
array[2] = "milk"
array.removeAll()
</pre>
<p>次のような結果が表示されるはずです。</p>
<pre class="brush:swift">["foo", "bar", "buzz", "coffee"]
["foo", "bar", "milk", "coffee"]
[]
</pre>
<h2 id="uitableview">UITableViewでの表示</h2>
<p><code>rx_itemsWithCellIdentifier</code>と組み合わせると、次のような感じで使えるようになるはずです。</p>
<pre class="brush:swift">model.rx_elements()
.observeOn(MainScheduler.instance)
.bindTo(tableView.rx_itemsWithCellIdentifier("MySampleCell")) { (row, element, cell) in
guard let c = cell as? MySampleCell else { return }
c.model = self.model[row]
return
}
.addDisposableTo(disposeBag)
</pre>
<h2 id="rx_events"><code>rx_events</code></h2>
<p><code>rx_events</code>は変化の差分の情報を配列を投げます。</p>
<pre class="brush:swift">ArrayChangeEvent(insertedIndeces: [3], deletedIndeces: [], updatedIndeces: [])
ArrayChangeEvent(insertedIndeces: [], deletedIndeces: [], updatedIndeces: [2])
ArrayChangeEvent(insertedIndeces: [], deletedIndeces: [0, 1, 2, 3], updatedIndeces: [])
</pre>
<p>この結果を見ればわかるように、インデックスベースでの結果が返されます。</p>
<p>なので、<code>UITableView</code>の<code>insertRowsAtIndexPaths</code>、<code>deleteRowsAtIndexPaths</code>、<code>reloadRowsAtIndexPaths</code>などを使ってアニメーションも頑張ればできます。</p>
<pre class="brush:swift">func toIndexSet(array: [Int]) -> [NSIndexPath] {
return array.map { NSIndexPath(forRow: $0, inSection: 0) }
}
self.beginUpdates()
self.insertRowsAtIndexPaths(toIndexSet(event.insertedIndeces), withRowAnimation: .Automatic)
self.deleteRowsAtIndexPaths(toIndexSet(event.deletedIndeces), withRowAnimation: .Automatic)
self.reloadRowsAtIndexPaths(toIndexSet(event.updatedIndeces), withRowAnimation: .Automatic)
self.endUpdates()
</pre>
<p>完全な例は<a href="https://github.com/safx/ObservableArray-RxSwift">GitHubのReadme</a>を参照してください。</p>
<p>ちなみに、<code>rx_itemsWithCellIdentifier</code>が内部で<code>reloadData()</code>を呼んでしまうので、
アニメーションをしたければ<code>rx_itemsWithCellIdentifier</code>と絡めて利用できませんのでご注意を。</p>
<h2 id="">その他</h2>
<p><code>CollectionType</code>準拠していたりするので<code>sort</code>なども使えます。</p>
<h2 id="">関連リンク</h2>
<ul>
<li><a href="http://safx-dev.blogspot.jp/2015/12/rxswiftretryretrywhen.html">Safx: RxSwiftでのretry、retryWhenを利用したエラー時の再実行について</a></li>
<li><a href="http://safx-dev.blogspot.jp/2014/12/auto-layoutvisual-format-languageswift.html">Safx: Auto LayoutのVisual Format Languageを楽に書くためのSwiftライブラリを書いた</a></li>
<li><a href="http://safx-dev.blogspot.jp/2014/01/vtacknowledgementsviewcontroller.html">Safx: 利用しているライブラリのライセンス一覧ビューを自動作成してくれるVTAcknowledgementsViewControllerを試す</a></li>
<li><a href="http://safx-dev.blogspot.jp/2014/10/swiftalamofiretypetalkapi.html">Safx: Swift+AlamofireでTypetalkのAPIを書いたので公開します</a></li>
</ul>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-60230000507681453702016-01-28T20:00:00.000+09:002016-01-28T20:00:11.476+09:00Cocoa勉強会関西で画像認識サービスとLightroomプラグインについて話しました
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj16diilu0WNidhtVNFCI2E7gKayL64zvZFfdV3taZoPUwz0mCrTwfFsdCX-HmW-Ls4fJiUEreDqAQTyAd_IRnky_ozeSSAmPpDuQIJ0p8SzgqafNsFTZGxfP83Iwhu5VYTyu69jHCo8xI/s1600/Screen+Shot+2016-01-28+at+19.27.13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj16diilu0WNidhtVNFCI2E7gKayL64zvZFfdV3taZoPUwz0mCrTwfFsdCX-HmW-Ls4fJiUEreDqAQTyAd_IRnky_ozeSSAmPpDuQIJ0p8SzgqafNsFTZGxfP83Iwhu5VYTyu69jHCo8xI/s400/Screen+Shot+2016-01-28+at+19.27.13.png" /></a></div>
<p>第65回 Cocoa勉強会関西で「画像認識サービスとLightroomプラグイン」というタイトルで話しました。</p>
<a name='more'></a>
<script async class="speakerdeck-embed" data-id="8d7338ed4fbf4ef783e55eb438515460" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"></script>
<p><a href="http://safx-dev.blogspot.jp/2016/01/clarifailightroom.html">写真のキーワードのサジェストをするLightroomプラグインをつくった</a>ので、それの紹介と現在ある画像認識サービスについて発表しました。</p>
<p>写真のキーワードを取る用途には<a href="http://www.clarifai.com/">Clarifai</a>がもっとも適していたので、Lightroomプラグインではこれを利用しました。</p>
<p>他の画像検索サービスなんかも、もっと使い込んでちゃんとお互いの優位点なんかを比較できたらなあと思っています。</p>
<p>質問にあったAKB48の顔認識ですが、Microsoft Project Oxfordの<a href="https://www.projectoxford.ai/demo/face#detection">Face Detection</a>で試したところ、2,3人認識できないことはありますが、結構な数を認識できているので普通に使えそうな印象でした。</p>
<p>LightroomのプラグインあたりのTipsもそれなりに溜っているのでブログにちょこっと書けたらなあと思っています。</p>
<h2 id="">関連リンク</h2>
<ul>
<li><a href="http://safx-dev.blogspot.jp/2016/01/clarifailightroom.html">Safx: 画像認識サービスClarifaiを利用してLightroomの写真にキーワードを付けるプラグインをつくった</a></li>
</ul>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-69750545113811406302016-01-19T10:00:00.000+09:002016-01-19T20:30:23.531+09:00画像認識サービスClarifaiを利用してLightroomの写真にキーワードを付けるプラグインをつくった<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6bHOVYqky7KPh0bl-5N9TvadpqCqLeLPGWaT4Y9bMefQbvIwRyZsHwK76RbgUryz0U1KEoWVpk2F4GA3SYaAPxI7q20ytMagJKAEe6AmMMI0k83NbDR1EAhFJA3fuq1YW3REvQy5dboM/s1600/ClarifaiTagger3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6bHOVYqky7KPh0bl-5N9TvadpqCqLeLPGWaT4Y9bMefQbvIwRyZsHwK76RbgUryz0U1KEoWVpk2F4GA3SYaAPxI7q20ytMagJKAEe6AmMMI0k83NbDR1EAhFJA3fuq1YW3REvQy5dboM/s700/ClarifaiTagger3.png" /></a></div>
<p>画像認識サービスの<a href="http://www.clarifai.com/">Clarifai</a>では、画像を送信するとその画像に適したキーワードを返してくれるAPIを提供しています。
これを利用して、Lightroomの写真にキーワードを付けるプラグインをつくってみました。</p>
<ul>
<li><a href="https://github.com/safx/LightroomPlugin-ClarifaiTagger">LightroomPlugin-ClarifaiTagger</a></li>
</ul>
<p>なお、このあたりの詳しい内容は1月23日に開催される<a href="http://cocoa-kansai.connpass.com/event/24558/">第65回 Cocoa勉強会関西</a>にて発表する予定です。</p>
<a name='more'></a>
<h2 id="">動作環境</h2>
<p>動作はMac OS X 10.11上のLightroom 5.7で確認しています。</p>
<p>それより前のバージョンでも動くかもしれませんが試していません。同様に、Windowsでも動くと思いますが試していません。</p>
<h2 id="">利用法</h2>
<ol>
<li>キーワードを付けたい画像を選択します。Clarifaiの制限で128枚以上を選択しないでください。</li>
<li>Lightroomメニュー → Library → Plugin-Extras → Open with Selected Photos</li>
<li>しばらく待つとキーワード選択ウィンドウが表示されますので、追加したいキーワードにチェックを入れてSaveをクリックしてください。</li>
</ol>
<h2 id="">導入方法</h2>
<ol>
<li><p><a href="http://www.clarifai.com/">Clarifai</a>にアカウント登録およびアプリケーションを作成する</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbuvXfXwEMzvO5Gfn-kuEUz-oPcnZF4SSqaMreNUKJkCMsziMvCBW82rWe2C530bYExZfeqH9xnMf1e6xug3F4sJmHjbKT-57q4YzhRXbf97Otrzx6jsHtSCWcq6Rw3oYMvmiqODinsns/s1600/ClarifaiApp2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbuvXfXwEMzvO5Gfn-kuEUz-oPcnZF4SSqaMreNUKJkCMsziMvCBW82rWe2C530bYExZfeqH9xnMf1e6xug3F4sJmHjbKT-57q4YzhRXbf97Otrzx6jsHtSCWcq6Rw3oYMvmiqODinsns/s400/ClarifaiApp2.png" /></a></div></li>
<li><p>Lightroomのプラグインマネージャから<code>ClarifaiTagger.lrdevplugin</code>を追加する</p></li>
<li><p>先ほど作成したアプリケーションのClient IDとClient Secretをプラグイン設定のClient IDとClient Secretに入れる</p>
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-GJxVpj1RMKQ/Vpz93C3ECUI/AAAAAAAADkc/PvtKHlEYd5c/s1600/PluginManager2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-GJxVpj1RMKQ/Vpz93C3ECUI/AAAAAAAADkc/PvtKHlEYd5c/s400/PluginManager2.png" /></a></div></li>
</ol>
<p>設定次第では英語のキーワードを返すようにもできます。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-lUSCm4INMPU/Vpz92c7doVI/AAAAAAAADkU/Y4Jpj6WESso/s1600/ClarifaiTagger1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-lUSCm4INMPU/Vpz92c7doVI/AAAAAAAADkU/Y4Jpj6WESso/s700/ClarifaiTagger1.png" /></a></div>
<h2 id="">関連リンク</h2>
<ul>
<li><a href="https://github.com/safx/LightroomPlugin-ClarifaiTagger">safx/LightroomPlugin-ClarifaiTagger</a></li>
<li><a href="http://www.clarifai.com/">Clarifai | Visual recognition API and services</a></li>
<li><a href="http://cocoa-kansai.connpass.com/event/24558/">第65回 Cocoa勉強会関西 - connpass</a></li>
</ul>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-62677075415227347932016-01-06T10:00:00.000+09:002016-01-06T10:00:25.590+09:002015年の人気記事まとめ<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-6aVJygvecV4/Vovij-CPPHI/AAAAAAAADjw/7XYUP9hYF8Q/s1600/P9281183.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-6aVJygvecV4/Vovij-CPPHI/AAAAAAAADjw/7XYUP9hYF8Q/s320/P9281183.jpg" /></a></div>
<p>あけましておめでとうございます。</p>
<p>本ブログで2015年に書かれたの記事を人気順にまとめました。</p>
<p>2015年は書いた記事が18本しかなかったので上位5件だけ紹介します。</p>
<a name='more'></a>
<h2 id="swiftassertpreconditionfatalerror">1. <a href="http://safx-dev.blogspot.jp/2015/02/swift-assert-precondition-and-fatalerror.html">SwiftでassertとpreconditionとfatalErrorをうまく使い分ける</a></h2>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8gWMgRgX3KyYjwAjWvgzVlGK5gmubdZOWxz9_MwXBY_TpZNg9cKtbpayaKXzL1KSDza6fx6w2vTZwqfzV4Qf0mML6s0yVzSeZjmuU4AMcEkplH4QU6hi5KZ_BUkLl8yzfrBUl3WtJTWU/s1600/assert_methods.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8gWMgRgX3KyYjwAjWvgzVlGK5gmubdZOWxz9_MwXBY_TpZNg9cKtbpayaKXzL1KSDza6fx6w2vTZwqfzV4Qf0mML6s0yVzSeZjmuU4AMcEkplH4QU6hi5KZ_BUkLl8yzfrBUl3WtJTWU/s800/assert_methods.png" /></a></div>
<p>Swiftに用意されているアサーション系メソッドである<code>assert</code>と<code>precondition</code>と<code>fatalError</code>の違いをまとめた記事。</p>
<p>この記事に書かれていない違いとしては、<code>fatalError</code>にだけ<code>@noreturn</code>が付いているという点もあります。
なので、<code>guard</code>節などで<code>return</code>せずに使えるのは<code>fatalError</code>だけです。</p>
<h2 id="ios9dnd6">2. <a href="http://safx-dev.blogspot.jp/2015/10/ios9-interapp-dnd.html">iOS 9でアプリ間のDnDができたので、関西モバイルアプリ研究会 #6で発表してきた</a></h2>
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-RszAFM_WsoI/VhEZfPdbuBI/AAAAAAAADgQ/UyQgVposz_M/s1600/ios9-dnd3.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-RszAFM_WsoI/VhEZfPdbuBI/AAAAAAAADgQ/UyQgVposz_M/s700/ios9-dnd3.gif" /></a></div>
<p>上のgifを見たらわかるように、完全にネタっぽい記事です。</p>
<p>ちなみに、昨年末ごろに、アプリ間のDnDも可能なライブラリが登場していますので、これを利用したらすぐに利用できるようになるかもしれません。</p>
<ul>
<li><a href="https://github.com/nevyn/CoreDragon">nevyn/CoreDragon · GitHub</a> (以前はSPDragNDropと呼ばれていた)</li>
</ul>
<p>CoreDragonは内部的にはUDPを利用したIPC上に、JSON風のデータを流すことでDnDデータのやりとりをしているようですので、知らないアプリ同士のDnDもできるのかもしれません。</p>
<p>ちなみに、アプリ間のDnDを利用したストアアプリは未だに見たことはないです。</p>
<h2 id="metalwaifu2x">3. <a href="http://safx-dev.blogspot.jp/2015/07/metalwaifu2x.html">Metalでwaifu2xを実装してみた</a></h2>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_dO6Q2FWFwLIPDjvSDPIdL77UEKx3Vv6bZ3GBOgXJ6Ikm7B1Vg30Zab6UNZs7uur9ygrTbY2HvtDi3lik_bN0npTVCWBsvnYekhz6_6JeefunAll1VzkXKNgZCawoHfZbeBMQr1L9qPo/s1600/waifu2x.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_dO6Q2FWFwLIPDjvSDPIdL77UEKx3Vv6bZ3GBOgXJ6Ikm7B1Vg30Zab6UNZs7uur9ygrTbY2HvtDi3lik_bN0npTVCWBsvnYekhz6_6JeefunAll1VzkXKNgZCawoHfZbeBMQr1L9qPo/s400/waifu2x.png" /></a></div>
<p>MacでMetalが使える!ということで試しに作ってみました、くらいの記事。</p>
<p>普通に利用するだけなら、他のものを利用したほうがよいです。</p>
<ul>
<li><a href="https://github.com/WL-Amigo/waifu2x-converter-cpp">WL-Amigo/waifu2x-converter-cpp · GitHub</a></li>
</ul>
<p>waifu2xの仕組みについては次の記事もどうぞ。</p>
<ul>
<li><a href="http://safx-dev.blogspot.jp/2015/07/62-cocoametalwaifu2x.html">第62回 Cocoa勉強会関西でMetalとwaifu2xについて話しました</a></li>
</ul>
<p>また、もっとシンプルなMetalコマンドラインアプリが見たいなら、こちらもどうぞ。</p>
<ul>
<li><a href="https://github.com/safx/Metal-CommandLine-Sample-Swift">safx/Metal-CommandLine-Sample-Swift · GitHub</a></li>
</ul>
<h2 id="rustgocrystalnimswift">4. <a href="http://safx-dev.blogspot.jp/2015/11/20-rust-go-crystal-nim-swift.html">20の言語/環境でてきとうにベンチマークしてみた (Rust, Go, Crystal, Nim, Swiftなど)</a></h2>
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-aTbRIkksE5Q/VjoBaCxFQ8I/AAAAAAAADjM/urPArxeRppw/s1600/Screen%2BShot%2B2015-11-04%2Bat%2B21.59.33.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-aTbRIkksE5Q/VjoBaCxFQ8I/AAAAAAAADjM/urPArxeRppw/s400/Screen%2BShot%2B2015-11-04%2Bat%2B21.59.33.png" /></a></div>
<p>Swiftってどのくらい早いんだろう?というところから調べはじめた記事。</p>
<p>記事にまとめるのにずいぶん手間どりましたが、いろいろな言語を触れてよかったです。
言語の違いをもっと感じられるほどのベンチマーク問題ならもっとよかったのでしょうけれど…。</p>
<h2 id="swiftstringinterpolationconvertible">5. <a href="http://safx-dev.blogspot.jp/2015/06/swift-string-interpolation-convertible.html">SwiftのStringInterpolationConvertibleプロトコルで文字列出力をカスタマイズする</a></h2>
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-iIVoy3ekP8U/VYF95RSTjGI/AAAAAAAADdY/4x44FZBvR1w/s1600/MyPlayground_playground.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-iIVoy3ekP8U/VYF95RSTjGI/AAAAAAAADdY/4x44FZBvR1w/s1600/MyPlayground_playground.png" /></a></div>
<p>使いかたのわかりづらい<code>StringInterpolationConvertible</code>の解説記事。</p>
<p><code>StringInterpolationConvertible</code>は使い処が難しいですよね。
よい利用法があったら教えて欲しいです。</p>
<p>本年もよろしくお願いいたします。</p>Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-38819405788451780772015-12-25T23:30:00.000+09:002015-12-25T23:30:15.275+09:00RxSwiftでのretry、retryWhenを利用したエラー時の再実行について
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiugwfFz0MPAL7N8AP3dq9dM0Am1QYVcVYzwMdiQ0PKqkPvEJj8Xfi84XufuCnk9eJqlQJihF9N1NFdVxdA91O39JOu-Y702dYHFOrrSiOOTZjTN-FZ3zcTMokreew42gugVuMvi15thOA/s1600/PB040137.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiugwfFz0MPAL7N8AP3dq9dM0Am1QYVcVYzwMdiQ0PKqkPvEJj8Xfi84XufuCnk9eJqlQJihF9N1NFdVxdA91O39JOu-Y702dYHFOrrSiOOTZjTN-FZ3zcTMokreew42gugVuMvi15thOA/s400/PB040137.jpg" /></a></div>
RxSwiftで`retry`、`retryWhen`を利用すると、処理の再実行やエラー処理がすっきり書けるようになるみたいなので試してみました。
<a name='more'></a>
<h2 id="retry">対象となるシーケンス</h2>
<p>次のような、3回やりなおさないと完了しないシーケンス (Observable) があるとします。</p>
<pre class="brush:swift">var count = 1
let sequence: Observable<Int> = create { observer in
let error = NSError(domain: "Test", code: 400 + count, userInfo: nil)
observer.on(.Next(1))
observer.on(.Next(2))
if count < 3 {
observer.on(.Error(error))
count++
}
observer.on(.Next(3))
observer.on(.Next(4))
observer.on(.Completed)
return NopDisposable.instance
}
</pre>
<p>これを完了させるようにするには、<code>retry</code>や<code>retryWhen</code>を使います。</p>
<h2 id="retry"><code>retry</code></h2>
<p>単純に失敗したら何回でもリトライしてよいなら、<code>retry</code>を使います。</p>
<pre class="brush:swift">let disposeBag = DisposeBag()
sequence
.retry()
.subscribe { print($0) }
.addDisposableTo(disposeBag)
</pre>
<p>実行結果は次のようになります。</p>
<pre class="brush:swift">Next(1)
Next(2)
Next(1)
Next(2)
Next(1)
Next(2)
Next(3)
Next(4)
Completed
</pre>
<h2 id="retryn"><code>retry(n)</code></h2>
<p>リトライする回数を指定することもできます。</p>
<pre class="brush:swift">sequence
.retry(2)
.subscribe { print($0) }
.addDisposableTo(disposeBag)
</pre>
<p>実行結果は次の通り。<code>retry(2)</code>なので、2回目の失敗で諦めてエラーを出していることがわかります。</p>
<pre class="brush:swift">Next(1)
Next(2)
Next(1)
Next(2)
Error(Error Domain=Test Code=402 "(null)")
</pre>
<h2 id="retrywhen"><code>retryWhen</code></h2>
<p>エラーコードが400番台のエラーはリトライしたいが、それ以外ではすぐに諦めてエラーを出したい、という状況を考えてみます。
このようなときは<code>retryWhen</code>を使います。</p>
<pre class="brush:swift">sequence
.retryWhen { (errors: Observable<ErrorType>) in
return errors.flatMap { err -> Observable<Int64> in
let e = err as NSError
if 400..<500 ~= e.code {
return just(0)
}
return failWith(err)
}
}
.subscribe { print($0) }
.addDisposableTo(disposeBag)
</pre>
<p><code>sequence</code>がエラーになる度に、<code>retryWhen</code>のクロージャ引数の<code>errors</code>にエラーが渡ってきます。
よって、エラーのシーケンス<code>errors</code>を<code>flatMap</code>などで処理し、そのときにエラーコードを見て適切な処理をするようにします (ちなみに、ここで<code>flatMap</code>の代わりに、<code>map</code>を使うとなぜか上手くいきませんでした)。</p>
<p>なお、<code>retryWhen</code>が実際にどのように処理をするのかは、次に示すように返すイベントの種類によって変わります (ここでは型は関係なく、<code>Int64</code>以外のものでも構いません)。</p>
<ul>
<li><code>.Next</code>のとき、<code>sequence</code>をリトライする</li>
<li><code>.Complete</code>のとき、<code>sequence</code>をリトライさせずに、完了させる</li>
<li><code>.Error</code>のとき、<code>sequence</code>をリトライさせずに、エラーにする</li>
</ul>
<p>これを上のコードに当てはめると、次のようになります。</p>
<ul>
<li>エラーコードが400番台のときは<code>.Next(0)</code>になるのでリトライ</li>
<li>それ以外は<code>.Error(err)</code>になるのでエラー</li>
</ul>
<h2 id="retrywhen"><code>retryWhen</code>でリトライ回数制限</h2>
<p>先ほどのコードにリトライの回数制限を設けてみます。つまり、次のような感じです。</p>
<ul>
<li>エラーコードが400番台のエラーは、2回までリトライするが、3回めには諦めてエラーにする</li>
<li>エラーコードが400番台のエラーではないなら、すぐにエラーにする</li>
</ul>
<p>これを実現する、とりあえずさっと思いつくコードは次のようになります。
外に<code>retryCount</code>があって汚ないですが、ともかくちゃんと動きます。</p>
<pre class="brush:swift">var retryCount = 0
let scheduler = SerialDispatchQueueScheduler(globalConcurrentQueuePriority: .Low)
sequence
.retryWhen { (errors: Observable<ErrorType>) in
return errors.flatMap { err -> Observable<Int64> in
let e = err as NSError
retryCount += 1
if 400..<500 ~= e.code && retryCount < 3 {
return timer(5, scheduler)
}
return failWith(err)
}
}
.subscribe { print($0) }
.addDisposableTo(disposeBag)
</pre>
<p>ちなみに、このコードでは<code>just(0)</code>を返す代わりに、<code>timer(5, scheduler)</code>を返すようにしています。
これによってリトライ前に5秒待つようになります。</p>
<h2 id="retrywhen2"><code>retryWhen</code>でリトライ回数制限 (2)</h2>
<p>先ほどのコードを<code>scan</code>で書き直してみます。
簡単に言うと<code>scan</code>はシーケンス向け<code>reduce</code>です。前の値と今の値が与えられるので、次の値を返していきます。</p>
<pre class="brush:swift">sequence
.retryWhen { (errors: Observable<ErrorType>) in
return errors.scan((0, nil)) { (a: (Int, ErrorType!), e) in
return (a.0 + 1, e)
}
.flatMap { retryCount, err -> Observable<Int64> in
let e = err as NSError
if 400..<500 ~= e.code && retryCount < 3 {
return timer(5, scheduler)
}
return failWith(err)
}
}
.subscribe { print($0) }
.addDisposableTo(disposeBag)
</pre>
<p>上では、初期値にタプル<code>(0, nil)</code>を与えています。これは「0回のエラー、最新のエラーは空」を意味します。
そして、エラーになる度にタプルの0番めの値を1増加させ、タプル1番めの値は最新のエラーに置き替えるようにしています。</p>
<p>多少変則的な<code>scan</code>の使いかたですが、外に変数がなくなった分すっきりしました。</p>
<h2 id="retrywhen3"><code>retryWhen</code>でリトライ回数制限 (3)</h2>
<p>実は<code>scan</code>を使わなくても、もっと綺麗に書く方法があります。
<code>flatMapWithIndex</code>という、シーケンスの要素と添字をタプルとして与えてくれるメソッドを使えばよいのです。</p>
<pre class="brush:swift">sequence
.retryWhen { (errors: Observable<ErrorType>) in
return errors
.flatMapWithIndex{ err, retryCount -> Observable<Int64> in
let e = err as NSError
if 400..<500 ~= e.code && retryCount < 3 {
return timer(5, scheduler)
}
return failWith(err)
}
}
.subscribe { print($0) }
.addDisposableTo(disposeBag)
</pre>
<p>これですっきりとわかりやすいコードになりました。</p>
<h2 id="">おわりに</h2>
<p>RxSwiftで<code>retry</code>、<code>retryWhen</code>を利用してエラー時の再実行を試してみました。</p>
<p>なお、リトライしないのであれば、<code>catchErrorJustReturn()</code>、<code>catchError()</code>というメソッドも利用できます。</p>
<h2 id="">関連項目</h2>
<ul>
<li><a href="https://github.com/ReactiveX/RxSwift/blob/master/Documentation/GettingStarted.md#implicit-observable-guarantees">RxSwift/GettingStarted</a></li>
<li><a href="http://reactivex.io/documentation/operators/retry.html">ReactiveX - Retry operator</a></li>
</ul>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-16643458951171094392015-11-05T10:00:00.000+09:002015-11-05T10:00:00.962+09:0020の言語/環境でてきとうにベンチマークしてみた (Rust, Go, Crystal, Nim, Swiftなど)<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-aTbRIkksE5Q/VjoBaCxFQ8I/AAAAAAAADjM/urPArxeRppw/s1600/Screen%2BShot%2B2015-11-04%2Bat%2B21.59.33.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-aTbRIkksE5Q/VjoBaCxFQ8I/AAAAAAAADjM/urPArxeRppw/s400/Screen%2BShot%2B2015-11-04%2Bat%2B21.59.33.png" /></a></div>
<p>自分が普段利用している言語や、気になっている言語などを集めてベンチマークを行いました。</p>
<p>次の2つのブログ記事の伝統に則り、再帰のフィボナッチ関数を使って、処理時間や利用メモリなどを計測してみました。</p>
<ul>
<li><a href="http://h-miyako.hatenablog.com/entry/2015/01/23/060000">この頃 流行りの 言語たち(他)でベンチマーク (Dart, Go, Julia, Nim, Python, Rust 他) - Blank File</a></li>
<li><a href="http://d.hatena.ne.jp/satosystems/20121228/1356655565">フィボナッチで各種言語をベンチマーク - satosystemsの日記</a></li>
</ul>
<p>ただし、値は埋め込みではなくて、コマンドライン引数として取れるように変更しています (定数で最適化されると嫌なので)。</p>
<a name='more'></a>
<style type="text/css">
.mytable {
border-spacing: 0px;
border-top: 1px solid #ccc;
border-left: 1px solid #ccc;
}
.mytable th, .mytable td {
border-right: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
.mytable th {
padding: 10px;
}
</style>
<h2 id="">言語紹介</h2>
<p>測定したのは、次の20言語です。</p>
<ul>
<li>C (Clang)</li>
<li>C++ (Clang)</li>
<li>Chapel</li>
<li>Crystal</li>
<li>D (DMD, LDC)</li>
<li>Elixir</li>
<li>Felix</li>
<li>Go</li>
<li>Julia</li>
<li>Lua (LuaJIT)</li>
<li>Nim</li>
<li>JavaScript (Node)</li>
<li>OCaml</li>
<li>PHP</li>
<li>Perl</li>
<li>Pony</li>
<li>Python (Python, Nuitka, PyPy)</li>
<li>Ruby</li>
<li>Rust</li>
<li>Swift</li>
</ul>
<p>Javaを入れるのが嫌なので、Java VM系の言語は一切ありません。</p>
<p>ソースなどは次のリポジトリにあります。</p>
<ul>
<li><a href="https://github.com/safx/fibonacci-benchmark">safx/fibonacci-benchmark · GitHub</a></li>
</ul>
<p>というわけで、各言語 (環境) を紹介。<a href="http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html">TIOBE Index</a> for October 2015のランク上位の言語から順に紹介していきます。</p>
<h2 id="c">C</h2>
<pre class="brush:plain">#include <stdio.h>
#include <stdlib.h>
int fib(int n) {
return n < 2 ? n : fib(n - 1) + fib(n - 2);
}
int main(int argc, char** argv) {
printf("%d\n", fib(atoi(argv[1])));
return 0;
}
</pre>
<p>他の言語もそうですが、コンパイルエラーにならない限りはエラー処理は極力無視するスタイルです。</p>
<p>コンパイルは<code>clang</code>なので<code>-Ofast</code>。</p>
<pre class="brush:plain">clang -Ofast -o fib-c fib.c
</pre>
<ul>
<li><a href="http://clang.llvm.org/">“clang” C Language Family Frontend for LLVM</a></li>
</ul>
<h2 id="c">C++</h2>
<p>19個だときりが悪いということで最後に急遽追加。</p>
<pre class="brush:plain">#include <iostream>
#include <string>
int fib(int n) {
return n < 2 ? n : fib(n - 1) + fib(n - 2);
}
int main(int argc, char** argv) {
std::cout << fib(std::stoi(argv[1])) << std::endl;
return 0;
}
</pre>
<p>C++11からstoiが使えるのでちょっと楽になりました。</p>
<p>コンパイルはCのときと同じ<code>-Ofast</code>。</p>
<pre class="brush:plain">clang++ -Ofast -o fib-c++ fib.cc
</pre>
<h2 id="python">Python</h2>
<p>スクリプト言語としては、一番よく利用して一番好きな言語です。
3項演算子の書きづらさだけはどうにかして欲しいですけれど。</p>
<p>Macに初期状態で入っている<code>python</code>をそのまま利用しています。</p>
<pre class="brush:plain">import sys
def fib(n):
return n if n < 2 else fib(n - 1) + fib(n - 2)
print(fib(int(sys.argv[1])))
</pre>
<p>実行時のオプション指定はなし。</p>
<pre class="brush:plain">python ./fib.py
</pre>
<ul>
<li><a href="https://www.python.org/">Welcome to Python.org</a></li>
</ul>
<h2 id="pypy">PyPy</h2>
<p>PythonのJIT実行環境。最近4.0.0が出ました。</p>
<p>ソースはPythonのをそのまま利用。</p>
<p>実行時オプションがいろいろ曲者で、設定次第で速度が倍以上違うので困りました。そこそこ速そうな<code>--jit function_threshold=5000</code>を利用しています。</p>
<pre class="brush:plain">$ time pypy ./fib.py 42
267914296
pypy ./fib.py 42 10.06s user 0.09s system 99% cpu 10.181 total
$ time pypy --jit function_threshold=5000 ./fib.py 42
267914296
pypy --jit function_threshold=5000 ./fib.py 42 3.79s user 0.07s system 99% cpu 3.878 total
</pre>
<ul>
<li><a href="http://pypy.org/">PyPy - Welcome to PyPy</a></li>
</ul>
<h2 id="nuitka">Nuitka</h2>
<p>ネイティブバイナリを作ることができるPythonコンパイラ、ということでPythonと比べてどれくらい早くなるかを試してみました。
ソースはPythonのものをそのまま利用しています。</p>
<p>コンパイルは<code>--clang</code>を付けましたが多分省略しても同じ。</p>
<pre class="brush:plain">nuitka --clang fib.py
</pre>
<ul>
<li><a href="http://nuitka.net/">Nuitka Home</a></li>
</ul>
<h2 id="php">PHP</h2>
<p>ハイパーテキスト向けのプリプロセッサ。
もう少しでPHP 7もリリースされるんですかね。
Macに初期状態で入っていたので、書きました。</p>
<pre class="brush:plain"><?php
function fib($n) {
return $n < 2 ? $n : fib($n - 1) + fib($n - 2);
}
echo fib($argv[1]), "\n";
?>
</pre>
<p>実行時のオプション指定はなし。</p>
<pre class="brush:plain">php ./fib.php
</pre>
<ul>
<li><a href="http://php.net/">PHP: Hypertext Preprocessor</a></li>
</ul>
<h2 id="node">Node</h2>
<p><a href="https://developers.google.com/v8/?hl=ja">Chrome V8</a>を搭載したJavaScript実行環境。</p>
<pre class="brush:plain">function fib(n) {
return n < 2 ? n : fib(n - 1) + fib(n - 2);
}
console.log(fib(process.argv[2]));
</pre>
<p>V8のオプションがいろいろあって死にそうになりましたが、ちょっといじってみても変わりがなかったので、オプションなしでデフォルトのまま実行しています。</p>
<pre class="brush:plain">node ./fib.js
</pre>
<ul>
<li><a href="https://nodejs.org/en/">Node.js</a></li>
</ul>
<h2 id="perl">Perl</h2>
<p>言わずと知れたスクリプト言語。蝶のロゴが可愛い<a href="http://perl6.org/">Perl 6</a>はPerl 5の妹らしい。
Macに初期状態で入っている<code>perl</code>をそのまま利用しています。</p>
<pre class="brush:plain">sub fib {
my $n = shift;
return $n < 2 ? $n : fib($n - 1) + fib($n - 2);
}
print fib($ARGV[0]), "\n";
</pre>
<p>実行時のオプション指定はなし。</p>
<pre class="brush:plain">perl ./fib.pl
</pre>
<ul>
<li><a href="https://www.perl.org/">The Perl Programming Language - www.perl.org</a></li>
</ul>
<p>ちなみに、Perl 6も試したのですが、Perl 5以上に猛烈に遅いので今回はベンチマーク対象にしていません (ソースはfib.pl6)。</p>
<pre class="brush:plain">$ time perl6 --optimize=3 fib.pl6 30
832040
perl6 --optimize=3 fib.pl6 30 13.19s user 0.32s system 99% cpu 13.539 total
$ time perl fib.pl 30
832040
perl fib.pl 30 0.83s user 0.01s system 99% cpu 0.845 total
</pre>
<h2 id="ruby">Ruby</h2>
<p>スクリプト言語。
Macに初期状態で入っている<code>ruby</code>をそのまま利用しています。</p>
<pre class="brush:plain">def fib(n)
if n < 2
n
else
fib(n - 1) + fib(n - 2)
end
end
puts fib(ARGV[0].to_i())
</pre>
<p>実行時のオプション指定はなし。</p>
<pre class="brush:plain">ruby ./fib.rb
</pre>
<ul>
<li><a href="https://www.ruby-lang.org/">Ruby Programming Language</a></li>
</ul>
<h2 id="crystal">Crystal</h2>
<p>Ruby作者のMatz氏も驚くRuby風のコンパイル言語。LLVM BC経由でネイティブバイナリを吐いてくれます。</p>
<blockquote class="twitter-tweet" lang="en"><p lang="ja" dir="ltr">これ、Rubyでもそのまま動くんじゃないか? Crystalすげーっ。 <a href="https://t.co/pO0F0vqTly">https://t.co/pO0F0vqTly</a></p>— Yukihiro Matsumoto (@yukihiro_matz) <a href="https://twitter.com/yukihiro_matz/status/610842781091672064">June 16, 2015</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>ソースはRubyのものをそのまま利用しています。</p>
<p>ちなみに、<a href="http://crystal-lang.org/docs/syntax_and_semantics/ternary_if.html">3項演算子あたりはRubyと違う</a>ようなので、両方で実行できるようなソースにしています。</p>
<p>コンパイル時オプションには<code>--release</code>。さらに、リンクエラーになったので<code>-L/usr/local/lib</code>を追加しています。</p>
<pre class="brush:plain">crystal build --link-flags -L/usr/local/lib --release -o fib-crystal fib.rb
</pre>
<ul>
<li><a href="http://crystal-lang.org/">Crystal</a></li>
</ul>
<h2 id="swift">Swift</h2>
<p>年末にはオープンソースになる予定のApple製の言語。
TIOBEでのランクもじわじわと上がっています。というかObjective-Cがランク下げすぎです。</p>
<pre class="brush:plain">func fib(n: Int) -> Int {
return n < 2 ? n : fib(n - 1) + fib(n - 2)
}
print(fib(Int(Process.arguments[1])!))
</pre>
<p>コンパイルオプションは<code>-O</code>。</p>
<pre class="brush:plain">swiftc -O -o fib-swift fib.swift
</pre>
<ul>
<li><a href="https://developer.apple.com/swift/">Swift - Overview - Apple Developer</a></li>
</ul>
<h2 id="d">D</h2>
<p>Facebookが手を入れ始めた言語。</p>
<pre class="brush:plain">import std.stdio;
import std.conv;
int fib(int n) {
return n < 2 ? n : fib(n - 1) + fib(n - 2);
}
void main(string[] args) {
writeln(fib(to!int(args[1])));
}
</pre>
<p>コンパイラはDMDとLDCの2種類あり、コンパイルオプションはそれぞれ次の通り。<code>-O5</code>とか実装があるのを始めて見ました。</p>
<pre class="brush:plain">dmd -m64 -release -O -offib-d fib.d
ldc2 -L=-w -O5 -m64 -offib-d-ldc fib.d
</pre>
<ul>
<li><a href="http://dlang.org/">Home - D Programming Language</a></li>
</ul>
<h2 id="luajit">LuaJIT</h2>
<p>軽量スクリプト言語として有名な<a href="http://www.lua.org/">Lua</a>のJITコンパイラです。</p>
<pre class="brush:plain">function fib(n)
if (n < 2) then
return n
else
return fib(n - 1) + fib(n - 2)
end
end
print(fib(tonumber(arg[1])))
</pre>
<p>実行時オプションは<code>-O3</code>。</p>
<pre class="brush:plain">luajit -O3 ./fib.lua
</pre>
<ul>
<li><a href="http://luajit.org/">The LuaJIT Project</a></li>
</ul>
<h2 id="elixir">Elixir</h2>
<p><a href="http://www.erlang.org/">Erlang</a> VM上で動作する関数型言語。
今回始めて書きましたが、モジュール宣言は必須なのかがいまいちわからず。</p>
<pre class="brush:plain">defmodule Fibonacci do
def fib(n) when n < 2 do
n
end
def fib(n) do
fib(n - 1) + fib(n - 2)
end
end
if System.argv != [] do
IO.puts Fibonacci.fib(String.to_integer(List.first(System.argv())))
end
</pre>
<p>実行時オプションは特になし。</p>
<pre class="brush:plain">elixir ./fib.exs
</pre>
<ul>
<li><a href="http://elixir-lang.org/">Elixir</a></li>
</ul>
<h2 id="ocaml">OCaml</h2>
<p>下で紹介しているFelixをコンパイルするための副産物として入ったので、ついでにコードも書いてみました。</p>
<p>今回始めて書いたので文法がわからずに苦労しました。</p>
<pre class="brush:plain">let rec fib n =
if n < 2 then n else fib (n - 1) + fib (n - 2);;
print_int (fib (int_of_string Sys.argv.(1)));
print_newline ()
</pre>
<p>最適化コンパイラの<code>ocamlopt</code>を使うのでコンパイラオプションは特になし。</p>
<pre class="brush:plain">ocamlopt -o fib-ocaml fib.ml
</pre>
<ul>
<li><a href="https://ocaml.org/">OCaml</a></li>
</ul>
<h2 id="rust">Rust</h2>
<p>Mozilla Research製の言語。<code>if let Some</code>のあたりはSwiftっぽいですね。</p>
<pre class="brush:plain">use std::env;
fn fib(n: isize) -> isize {
if n < 2 {
n
} else {
fib(n - 1) + fib(n - 2)
}
}
fn main() {
if let Some(s) = env::args().nth(1) {
if let Ok(n) = s.parse::<isize>() {
println!("{}", fib(n));
}
}
}
</pre>
<p>コンパイルオプションは<code>-O</code>のみ。</p>
<pre class="brush:plain">rustc -O -o fib-rust fib.rs
</pre>
<ul>
<li><a href="https://www.rust-lang.org/">The Rust Programming Language</a></li>
</ul>
<h2 id="go">Go</h2>
<p>TIOBEでのランクだと50位より下だったことにちょっと驚いてしまったGoogle製の言語。
マスコットキャラのGopherが可愛い。</p>
<pre class="brush:plain">package main
import (
"fmt"
"os"
"strconv"
)
func fib(a int) int {
if a < 2 {
return a
} else {
return fib(a - 1) + fib(a - 2)
}
}
func main() {
n, _ := strconv.Atoi(os.Args[1])
fmt.Println(fib(n))
}
</pre>
<p>コンパイルオプションはないっぽい。<code>build</code>にすると最適化されているのだろうか…。</p>
<pre class="brush:plain">go build -o fib-go fib.go
</pre>
<ul>
<li><a href="https://golang.org/">The Go Programming Language</a></li>
</ul>
<h2 id="julia">Julia</h2>
<p>MathematicaやMATLABの流れを汲む科学計算向けの言語、らしい。</p>
<pre class="brush:plain">fib(n) = n < 2 ? n : fib(n - 1) + fib(n - 2)
println(fib(parse(ARGS[1])))
</pre>
<p>実行時には最適化オプションの<code>-O</code>を追加。</p>
<pre class="brush:plain">julia -O ./fib.jl
</pre>
<ul>
<li><a href="http://julialang.org/">The Julia Language</a></li>
</ul>
<h2 id="nim">Nim</h2>
<p>マルチパラダイム言語。昔はPascalで書かれていたらしい。確かに<code>result</code>に代入できたりするところがPascalっぽい。
デフォルトではCソースを吐いてからそれをコンパイルしています。</p>
<pre class="brush:plain">import os
import strutils
proc fib(n: int): int =
result = if n < 2: n else: fib(n - 1) + fib(n - 2)
echo(fib(parseInt(paramStr(1))))
</pre>
<p>コンパイルオプションはいろいろ付けていますが、<code>-d:release</code>が重要。</p>
<pre class="brush:plain">nim c --verbosity:0 -d:release --app:console --opt:speed --out:fib-nim fib.nim
</pre>
<ul>
<li><a href="http://nim-lang.org/">Nim Programming Language</a></li>
</ul>
<h2 id="chapel">Chapel</h2>
<p>ここからマイナー言語の紹介。といっても、これはGitHubの<a href="https://github.com/trending?l=chapel">Trending repositories</a>に名前があるだけ有名なほうです。</p>
<p>スーパーコンピュータで有名な<a href="https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AC%E3%82%A4">クレイ</a>がつくった並列計算のための言語 (<a href="https://github.com/chapel-lang">ソースはGitHubで公開</a>)。
2004年くらいから開発が始まっているみたいですが、息が長い割にまったく名前を聞いたことがなかったです。</p>
<pre class="brush:plain">proc fib(n): int return if n < 2 then n else fib(n - 1) + fib(n - 2);
config var n = 1;
writeln(fib(n));
</pre>
<p>おもしろいことに、グローバル変数の宣言に<code>config</code>と付けると、コマンドライン引数と見なしてくれる機能があること (上の例だと、<code>--n=4</code>みたいに指定できるようになる)。
まさに、コマンドラインツールを書くための言語。</p>
<p>コンパイル引数はよくありがちな<code>-O</code>。</p>
<pre class="brush:plain">chpl -O -o fib-chapel fib.chpl
</pre>
<ul>
<li><a href="http://chapel.cray.com/">Chapel: Productive Parallel Programming</a></li>
</ul>
<h2 id="pony">Pony</h2>
<p>Erlangのようにアクターモデルを持ちつつ、LLVMベースでネイティブバイナリにコンパイルできるため、実行パフォーマンスがよい言語。</p>
<pre class="brush:plain">actor Main
fun fib(n: U32): U32 =>
if n < 2 then
return n
else
return fib(n - 1) + fib(n - 2)
end
new create(env: Env) =>
let n: U32 = try env.args(1).u32() else 0 end
env.out.print(fib(n).string())
</pre>
<p>コンパイル引数はちょっと特殊で<code>ponyc</code>のみでよく、カレントディレクトリのソースを捜索してコンパイルするみたいです。</p>
<pre class="brush:plain">ponyc
</pre>
<ul>
<li><a href="http://www.ponylang.org/">Pony - High Performance Actor Programming</a></li>
</ul>
<h2 id="felix">Felix</h2>
<p>恐らく知っている人はいないであろう超マイナー言語。言語のロゴはこれでいいのか…?
5年ぶりくらいにメジャーアップデートが出そうな気配です。言語的にはC++のトランスレータ的な趣きが強い。
C++との連携にグルーコードが不要とか、Cよりも早いバイナリを作るよとか書かれていて気になったので試してみました。</p>
<p>最近、<a href="https://github.com/felix-lang/felix/releases/tag/2015.10.30-rc8">WindowsとLinuxにはバイナリが用意されるようになった</a>みたいですが、
Macではバイナリがない上に2015.10.30-rc8だとコンパイルエラーになるので、どうにかコンパイルできたコミット2b4c4a7のバイナリを利用しています。</p>
<pre class="brush:plain">fun fib (n:int) =>
if n < 2 then n else fib(n - 1) + fib(n - 2) endif;
println $ str $ fib $ int $ System::argv 1;
</pre>
<p>コンパイルオプションは次の通り。</p>
<pre class="brush:plain">flx --static -c -O3 -o fib-felix fib.flx
</pre>
<ul>
<li><a href="https://github.com/felix-lang/felix">felix-lang/felix · GitHub</a></li>
</ul>
<h2 id="">速度</h2>
<p>上で紹介した<a href="http://h-miyako.hatenablog.com/entry/2015/01/23/060000">ベンチマーク記事</a>が<a href="https://ja.wikipedia.org/wiki/%E7%94%9F%E5%91%BD%E3%80%81%E5%AE%87%E5%AE%99%E3%80%81%E3%81%9D%E3%81%97%E3%81%A6%E4%B8%87%E7%89%A9%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E3%81%AE%E7%A9%B6%E6%A5%B5%E3%81%AE%E7%96%91%E5%95%8F%E3%81%AE%E7%AD%94%E3%81%88">42</a>を使っていたので、各言語でfib(42)した結果をグラフにまとめました。</p>
<p><iframe width="1919" height="600" seamless frameborder="0" scrolling="no" src="https://docs.google.com/spreadsheets/d/13JiVbCOcRC8288kmCtPGJh-1hDajsgN1oJz8kHsDZ3s/pubchart?oid=238330652&format=interactive"></iframe></p>
<p>コンパイル言語のみでまとめなおしたものがこちら。最速はOCamlで、続いてSwift, Rust, D (LDC), Felix, Crystal, Cとなりました。
OCamlとSwiftが一歩だけ抜けて早いかんじです。使っている分にはSwiftが早い感じはあまりしませんけれど。</p>
<p><iframe width="900" height="400" seamless frameborder="0" scrolling="no" src="https://docs.google.com/spreadsheets/d/13JiVbCOcRC8288kmCtPGJh-1hDajsgN1oJz8kHsDZ3s/pubchart?oid=1743930609&format=interactive"></iframe></p>
<p>fib(1)のときはあまり処理がないので、だいたい起動処理時間と思ってよいはず…ということでスクリプト言語の起動時間としてまとめたものがこちら。コンパイル言語はすべて起動時間0なので省略しています。
Perlの圧倒的な起動時間の速さに驚かされます。</p>
<p><iframe width="800" height="420" seamless frameborder="0" scrolling="no" src="https://docs.google.com/spreadsheets/d/13JiVbCOcRC8288kmCtPGJh-1hDajsgN1oJz8kHsDZ3s/pubchart?oid=1653081929&format=interactive"></iframe></p>
<p>で、フィボナッチの値を1から45 (スクリプト言語は42) まで変化させてみたときの結果は次の通り。
全てを覆い、天を突き抜けようとしているのがPerlです。
PyPyは処理が多くなると最適化をかけ直したりするのか、処理時間が安定しません。</p>
<p><iframe width="1040" height="868" seamless frameborder="0" scrolling="no" src="https://docs.google.com/spreadsheets/d/13JiVbCOcRC8288kmCtPGJh-1hDajsgN1oJz8kHsDZ3s/pubchart?oid=77025784&format=interactive"></iframe></p>
<p>上のグラフを、右下のみを拡大させた結果は次のようになります。</p>
<p><iframe width="1040" height="868" seamless frameborder="0" scrolling="no" src="https://docs.google.com/spreadsheets/d/13JiVbCOcRC8288kmCtPGJh-1hDajsgN1oJz8kHsDZ3s/pubchart?oid=2140760139&format=interactive"></iframe></p>
<p>全体を対数グラフにしたものがこちら。Juliaの「ウサギとカメ」のカメ感がなかなかよいです。</p>
<p><iframe width="1040" height="868" seamless frameborder="0" scrolling="no" src="https://docs.google.com/spreadsheets/d/13JiVbCOcRC8288kmCtPGJh-1hDajsgN1oJz8kHsDZ3s/pubchart?oid=1696451278&format=interactive"></iframe></p>
<p>右下のみを拡大して見やすくしたものがこちら。
こうして見ると、OCamlとSwiftの2つはグラフが被っていて、だいたい同じ結果になっていることがわかります。</p>
<p><iframe width="1040" height="868" seamless frameborder="0" scrolling="no" src="https://docs.google.com/spreadsheets/d/13JiVbCOcRC8288kmCtPGJh-1hDajsgN1oJz8kHsDZ3s/pubchart?oid=1560032372&format=interactive"></iframe></p>
<ul>
<li><a href="https://docs.google.com/spreadsheets/d/13JiVbCOcRC8288kmCtPGJh-1hDajsgN1oJz8kHsDZ3s/pubhtml">元データの表へのリンク</a></li>
</ul>
<h2 id="">実行時メモリ使用量</h2>
<p>それぞれ、<code>fib(35)</code>の値を取るときに<code>/usr/bin/time -l</code>して、そのmaximum resident set sizeの値を結果としました。Juliaがメモリ食いすぎでした。</p>
<table class="mytable">
<colgroup>
<col style="text-align:left;"/>
<col style="text-align:right;"/>
</colgroup>
<thead>
<tr>
<th style="text-align:left;">言語/環境</th>
<th style="text-align:right;">サイズ (バイト)</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;">C</td>
<td style="text-align:right;">688,128</td>
</tr>
<tr>
<td style="text-align:left;">C++</td>
<td style="text-align:right;">692,224</td>
</tr>
<tr>
<td style="text-align:left;">Felix</td>
<td style="text-align:right;">794,624</td>
</tr>
<tr>
<td style="text-align:left;">Nim</td>
<td style="text-align:right;">819,200</td>
</tr>
<tr>
<td style="text-align:left;">OCaml</td>
<td style="text-align:right;">864,256</td>
</tr>
<tr>
<td style="text-align:left;">Pony</td>
<td style="text-align:right;">995,328</td>
</tr>
<tr>
<td style="text-align:left;">LuaJIT</td>
<td style="text-align:right;">1,134,592</td>
</tr>
<tr>
<td style="text-align:left;">Rust</td>
<td style="text-align:right;">1,134,592</td>
</tr>
<tr>
<td style="text-align:left;">D (DMD)</td>
<td style="text-align:right;">1,142,784</td>
</tr>
<tr>
<td style="text-align:left;">Crystal</td>
<td style="text-align:right;">1,236,992</td>
</tr>
<tr>
<td style="text-align:left;">D (LDC)</td>
<td style="text-align:right;">1,327,104</td>
</tr>
<tr>
<td style="text-align:left;">Go</td>
<td style="text-align:right;">1,462,272</td>
</tr>
<tr>
<td style="text-align:left;">Perl</td>
<td style="text-align:right;">1,683,456</td>
</tr>
<tr>
<td style="text-align:left;">Chapel</td>
<td style="text-align:right;">1,839,104</td>
</tr>
<tr>
<td style="text-align:left;">Swift</td>
<td style="text-align:right;">3,629,056</td>
</tr>
<tr>
<td style="text-align:left;">Python</td>
<td style="text-align:right;">4,759,552</td>
</tr>
<tr>
<td style="text-align:left;">Nuitka</td>
<td style="text-align:right;">4,849,664</td>
</tr>
<tr>
<td style="text-align:left;">Ruby</td>
<td style="text-align:right;">7,135,232</td>
</tr>
<tr>
<td style="text-align:left;">PHP</td>
<td style="text-align:right;">9,342,976</td>
</tr>
<tr>
<td style="text-align:left;">Node</td>
<td style="text-align:right;">18,325,504</td>
</tr>
<tr>
<td style="text-align:left;">Elixir</td>
<td style="text-align:right;">33,153,024</td>
</tr>
<tr>
<td style="text-align:left;">PyPy</td>
<td style="text-align:right;">51,376,128</td>
</tr>
<tr>
<td style="text-align:left;">Julia</td>
<td style="text-align:right;">97,636,352</td>
</tr>
</tbody>
</table>
<h2 id="">コンパイル時間</h2>
<p>コンパイラ言語のみ、コンパイル時間を計測してみました。
直前に<code>make && make clean</code>してからの結果。</p>
<table class="mytable">
<colgroup>
<col style="text-align:left;"/>
<col style="text-align:right;"/>
</colgroup>
<thead>
<tr>
<th style="text-align:left;">言語/環境</th>
<th style="text-align:right;">時間 (秒)</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;">C</td>
<td style="text-align:right;">0.06</td>
</tr>
<tr>
<td style="text-align:left;">OCaml</td>
<td style="text-align:right;">0.12</td>
</tr>
<tr>
<td style="text-align:left;">Felix</td>
<td style="text-align:right;">0.23</td>
</tr>
<tr>
<td style="text-align:left;">D (dmd)</td>
<td style="text-align:right;">0.25</td>
</tr>
<tr>
<td style="text-align:left;">Rust</td>
<td style="text-align:right;">0.26</td>
</tr>
<tr>
<td style="text-align:left;">Swift</td>
<td style="text-align:right;">0.26</td>
</tr>
<tr>
<td style="text-align:left;">Go</td>
<td style="text-align:right;">0.37</td>
</tr>
<tr>
<td style="text-align:left;">C++</td>
<td style="text-align:right;">0.42</td>
</tr>
<tr>
<td style="text-align:left;">D (ldc)</td>
<td style="text-align:right;">0.64</td>
</tr>
<tr>
<td style="text-align:left;">Nim</td>
<td style="text-align:right;">0.68</td>
</tr>
<tr>
<td style="text-align:left;">Nuitka</td>
<td style="text-align:right;">1.57</td>
</tr>
<tr>
<td style="text-align:left;">Crystal</td>
<td style="text-align:right;">2.06</td>
</tr>
<tr>
<td style="text-align:left;">Pony</td>
<td style="text-align:right;">2.42</td>
</tr>
<tr>
<td style="text-align:left;">Chapel</td>
<td style="text-align:right;">3.26</td>
</tr>
</tbody>
</table>
<h2 id="">バイナリ</h2>
<p>コンパイラ言語のバイナリの状況です。</p>
<p>まずはファイルサイズ。Cは群を抜いて小さいですね。</p>
<table class="mytable">
<colgroup>
<col style="text-align:left;"/>
<col style="text-align:right;"/>
</colgroup>
<thead>
<tr>
<th style="text-align:left;">言語/環境</th>
<th style="text-align:right;">サイズ (バイト)</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;">C</td>
<td style="text-align:right;">8,504</td>
</tr>
<tr>
<td style="text-align:left;">C++</td>
<td style="text-align:right;">10,180</td>
</tr>
<tr>
<td style="text-align:left;">Swift</td>
<td style="text-align:right;">17,560</td>
</tr>
<tr>
<td style="text-align:left;">Nim</td>
<td style="text-align:right;">42,896</td>
</tr>
<tr>
<td style="text-align:left;">Crystal</td>
<td style="text-align:right;">65,580</td>
</tr>
<tr>
<td style="text-align:left;">Pony</td>
<td style="text-align:right;">82,432</td>
</tr>
<tr>
<td style="text-align:left;">Nuitka</td>
<td style="text-align:right;">88,200</td>
</tr>
<tr>
<td style="text-align:left;">OCaml</td>
<td style="text-align:right;">187,596</td>
</tr>
<tr>
<td style="text-align:left;">Rust</td>
<td style="text-align:right;">277,604</td>
</tr>
<tr>
<td style="text-align:left;">Felix</td>
<td style="text-align:right;">497,088</td>
</tr>
<tr>
<td style="text-align:left;">Chapel</td>
<td style="text-align:right;">520,132</td>
</tr>
<tr>
<td style="text-align:left;">D (dmd)</td>
<td style="text-align:right;">594,608</td>
</tr>
<tr>
<td style="text-align:left;">D (ldc)</td>
<td style="text-align:right;">2,160,220</td>
</tr>
<tr>
<td style="text-align:left;">Go</td>
<td style="text-align:right;">2,324,112</td>
</tr>
</tbody>
</table>
<p>続いて、依存ライブラリ。
Goは何にも依存していません。これならバイナリサイズが大きいのも納得です。
libSystem.B.dylibは他の環境で言うところのlibcに当たるものらしいので、C言語やC言語に変換系の言語ではこれに依存しています。</p>
<pre class="brush:plain">fib-go:
fib-c:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
fib-d:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
fib-d-ldc:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
fib-nim:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
fib-rust:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
fib-ocaml:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
fib-pony:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
fib-c++:
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.1.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
fib-felix:
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.1.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
fib-swift:
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
@rpath/libswiftCore.dylib (compatibility version 0.0.0, current version 0.0.0)
fib-nuitka:
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.1.0)
/System/Library/Frameworks/Python.framework/Versions/2.7/Python (compatibility version 2.7.0, current version 2.7.10)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
fib-chapel:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
/usr/local/opt/chapel/libexec/third-party/gmp/install/darwin-clang-native/lib/libgmp.10.dylib (compatibility version 13.0.0, current version 13.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.1.0)
fib-crystal:
/usr/local/opt/libevent/lib/libevent-2.0.5.dylib (compatibility version 7.0.0, current version 7.9.0)
/usr/local/opt/libpcl/lib/libpcl.1.dylib (compatibility version 2.0.0, current version 2.11.0)
/usr/lib/libpcre.0.dylib (compatibility version 1.0.0, current version 1.1.0)
/usr/local/opt/bdw-gc/lib/libgc.1.dylib (compatibility version 2.0.0, current version 2.3.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
</pre>
<p>どれも問題なく64-bitバイナリでした。</p>
<pre class="brush:plain">fib-c: Mach-O 64-bit executable x86_64
fib-c++: Mach-O 64-bit executable x86_64
fib-chapel: Mach-O 64-bit executable x86_64
fib-d: Mach-O 64-bit executable x86_64
fib-d-ldc: Mach-O 64-bit executable x86_64
fib-crystal: Mach-O 64-bit executable x86_64
fib-go: Mach-O 64-bit executable x86_64
fib-felix: Mach-O 64-bit executable x86_64
fib-nim: Mach-O 64-bit executable x86_64
fib-rust: Mach-O 64-bit executable x86_64
fib-swift: Mach-O 64-bit executable x86_64
fib-ocaml: Mach-O 64-bit executable x86_64
fib-nuitka: Mach-O 64-bit executable x86_64
fib-pony: Mach-O 64-bit executable x86_64
</pre>
<h2 id="">雑感</h2>
<ul>
<li>最速はOCamlとSwift</li>
<li>Goのシングルバイナリ感がすごい</li>
<li>Perlは実行速度は遅いが、起動の軽さとメモリ利用量の少なさがよい</li>
<li>PythonはRubyより遅いのね…</li>
<li>Rustは1.4.0になって、Cと同等の速度になった (1.3.0はCの4割くらい遅かった)</li>
<li>CrystalはだいたいC同じくらいの速度。ただしコンパイルは遅い</li>
<li>Juliaは起動が遅いが、それ以降はコンパイラ言語並みに早い</li>
<li>PyPyの速度の不安定さ。ちゃんと使うならチューニング必須</li>
<li>Luaのいろいろな軽さ</li>
</ul>
<h2 id="">バージョン</h2>
<p>基本的に、brew, brew cask, pipを利用しております。</p>
<ul>
<li>Rust: 最新版パッケージのなかったので公式サイトよりパッケージインストール</li>
<li>Nim: 最新版パッケージのなかったので公式サイトよりビルド</li>
<li>Felix: パッケージのないのでソースよりビルド (コミット2b4c4a7)</li>
</ul>
<table class="mytable">
<colgroup>
<col style="text-align:left;"/>
<col style="text-align:left;"/>
</colgroup>
<thead>
<tr>
<th style="text-align:left;">言語/環境</th>
<th style="text-align:left;">バージョン</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;">C, C++ (Clang)</td>
<td style="text-align:left;">Apple LLVM version 7.0.0 (clang–700.1.76)</td>
</tr>
<tr>
<td style="text-align:left;">Chapel</td>
<td style="text-align:left;">chpl Version 1.12.0</td>
</tr>
<tr>
<td style="text-align:left;">Crystal</td>
<td style="text-align:left;">Crystal 0.9.1 (Fri Oct 30 13:49:50 UTC 2015)</td>
</tr>
<tr>
<td style="text-align:left;">D (dmd)</td>
<td style="text-align:left;">DMD64 D Compiler v2.068</td>
</tr>
<tr>
<td style="text-align:left;">D (ldc)</td>
<td style="text-align:left;">LDC - the LLVM D compiler (0.16.0):</td>
</tr>
<tr>
<td style="text-align:left;">Elixir</td>
<td style="text-align:left;">Elixir 1.1.1</td>
</tr>
<tr>
<td style="text-align:left;">Felix</td>
<td style="text-align:left;">version 15.08.15</td>
</tr>
<tr>
<td style="text-align:left;">Go</td>
<td style="text-align:left;">go version go1.5.1 darwin/amd64</td>
</tr>
<tr>
<td style="text-align:left;">Julia</td>
<td style="text-align:left;">julia version 0.4.0</td>
</tr>
<tr>
<td style="text-align:left;">LuaJIT</td>
<td style="text-align:left;">LuaJIT 2.0.4 – Copyright (C) 2005–2015 Mike Pall. http://luajit.org/</td>
</tr>
<tr>
<td style="text-align:left;">Nim</td>
<td style="text-align:left;">Nim Compiler Version 0.12.0 (2015–10–27) [MacOSX: amd64]</td>
</tr>
<tr>
<td style="text-align:left;">Node</td>
<td style="text-align:left;">v5.0.0</td>
</tr>
<tr>
<td style="text-align:left;">Nuitka</td>
<td style="text-align:left;">0.5.15</td>
</tr>
<tr>
<td style="text-align:left;">OCaml</td>
<td style="text-align:left;">The OCaml toplevel, version 4.02.3</td>
</tr>
<tr>
<td style="text-align:left;">PHP</td>
<td style="text-align:left;">PHP 5.5.27 (cli) (built: Aug 22 2015 18:31:33)</td>
</tr>
<tr>
<td style="text-align:left;">Perl</td>
<td style="text-align:left;">This is perl 5, version 18, subversion 2 (v5.18.2) built for darwin-thread-multi–2level</td>
</tr>
<tr>
<td style="text-align:left;">Pony</td>
<td style="text-align:left;">0.2.1</td>
</tr>
<tr>
<td style="text-align:left;">Python</td>
<td style="text-align:left;">Python 2.7.10</td>
</tr>
<tr>
<td style="text-align:left;">PyPy</td>
<td style="text-align:left;">[PyPy 4.0.0 with GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang–700.1.76)]</td>
</tr>
<tr>
<td style="text-align:left;">Ruby</td>
<td style="text-align:left;">ruby 2.0.0p645 (2015–04–13 revision 50299) [universal.x86_64-darwin15]</td>
</tr>
<tr>
<td style="text-align:left;">Rust</td>
<td style="text-align:left;">rustc 1.4.0 (8ab8581f6 2015–10–27)</td>
</tr>
<tr>
<td style="text-align:left;">Swift</td>
<td style="text-align:left;">Apple Swift version 2.1 (swiftlang–700.1.101.6 clang–700.1.76)</td>
</tr>
</tbody>
</table>
<h2 id="">測定環境</h2>
<ul>
<li>MacBook Pro (Retina, Mid 2012)</li>
<li>Processor 2.6 GHz Intel Core i7</li>
<li>Memory 16 GB 1600 MHz DDR3</li>
<li>OS OS X El Capitan (15A279b)</li>
</ul>
<h2 id="">関連項目</h2>
<ul>
<li><a href="https://github.com/safx/fibonacci-benchmark">safx/fibonacci-benchmark · GitHub</a></li>
<li><a href="http://h-miyako.hatenablog.com/entry/2015/01/23/060000">この頃 流行りの 言語たち(他)でベンチマーク (Dart, Go, Julia, Nim, Python, Rust 他) - Blank File</a></li>
<li><a href="http://d.hatena.ne.jp/satosystems/20121228/1356655565">フィボナッチで各種言語をベンチマーク - satosystemsの日記</a></li>
<li><a href="http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html">TIOBE Software: Tiobe Index</a></li>
<li><a href="https://github.com/nsf/pnoise">nsf/pnoise · GitHub</a></li>
</ul>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-32288099359340048562015-10-30T10:00:00.000+09:002015-12-28T00:44:25.860+09:00FastlaneのsnapshotがUIテストに対応したので試してみた (関西モバイルアプリ研究会 #7)
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-0P8ucJwQ_0c/VjI255Uf4pI/AAAAAAAADiw/ESqOYU4GBbs/s1600/Screen%2BShot%2B2015-10-29%2Bat%2B23.43.24.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-0P8ucJwQ_0c/VjI255Uf4pI/AAAAAAAADiw/ESqOYU4GBbs/s400/Screen%2BShot%2B2015-10-29%2Bat%2B23.43.24.png" /></a></div>
<p>アプリのスクリーンショットを撮るためのツールである、<a href="https://fastlane.tools/">fastlane</a>の<a href="https://github.com/fastlane/snapshot">snapshot</a>がUIテストに対応したので試してみました、という発表を<a href="http://kanmoba.connpass.com/event/21395/">関西モバイルアプリ研究会 #7</a>でしてきました。</p>
<a name='more'></a>
<script async class="speakerdeck-embed" data-id="e119424d924949e4bd2185aca419161c" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"></script>
<h2 id="snapshot">snapshotへの対応</h2>
<p><code>snapshot</code>への対応は、だいたい次のようになります。</p>
<ol>
<li><p><code>snapshot init</code></p></li>
<li><p>作られた<code>SnapshotHelper.swift</code>をUIテストに追加する</p></li>
<li><p>作られた<code>Snapfile</code>を編集する</p></li>
<li><p>UIテストの<code>setUp()</code>に次のコードを追加する</p>
<pre class="brush:swift">let app = XCUIApplication()
setLanguage(app)
app.launch()
</pre></li>
<li><p>通常の手順でUIテストを記録</p></li>
<li><p>スクリーンショットを撮りたいところで、<code>snapshot("0Launch")</code>みたいなコードを追加する</p></li>
</ol>
<h2 id="tips">Tips</h2>
<h3 id="ui">UIテスト中であることをターゲットアプリ側で認識</h3>
<p><code>XCUIApplication.launchArguments</code>を利用するとターゲットアプリに引数を渡して起動できます。</p>
<pre class="brush:swift">let app = XCUIApplication()
setLanguage(app)
app.launchArguments += ["--snapshot"] // 追加
app.launch()
</pre>
<p>その引数は<code>NSProcessInfo.arguments</code>で取れるので、そこでチェックしましょう。</p>
<pre class="brush:swift">if NSProcessInfo().arguments.indexOf("--snapshot") != nil {
// code for initilizing
}
</pre>
<p>これを使うと<code>application:didFinishLaunchingWithOptions:</code>のタイミングでsnapshotを撮る用のデータを用意したりすることができるようになります。</p>
<h3 id="">タブバーなどでスクリーンショットがうまく撮れない</h3>
<blockquote>
<b>2015.12.26 追記</b> このバグはFastlane 1.2.0で修正されました。
</blockquote>
<p>UIテストでは、基本的にUI操作ごとに自動でキャプチャを撮るようになっています。</p>
<p><code>snapshot()</code>では、<code>snapshot</code>のためのキャプチャであるとわかるように、画面外でのスワイプを行っています。</p>
<pre class="brush:swift">let view = XCUIApplication()
let start = view.coordinateWithNormalizedOffset(CGVectorMake(32.10, 30000))
let finish = view.coordinateWithNormalizedOffset(CGVectorMake(31, 30000))
start.pressForDuration(0, thenDragToCoordinate: finish)
</pre>
<p>しかし、これがシミュレータ上では画面右端のタップと勘違いされてしまうので、タブの切り替えが発生してしまいます。</p>
<p>これを防ぐには次のようなコードを<code>application:didFinishLaunchingWithOptions:</code>や<code>viewDidLoad:</code>に入れるとよいです。</p>
<pre class="brush:swift">if let rootView = window?.rootViewController?.view
where NSProcessInfo().arguments.indexOf("--snapshot") != nil {
let s = rootView.frame.size
let view = UIView(frame: CGRectMake(s.width - 1, s.height - 1, 1, 1))
rootView.addSubview(view)
}
</pre>
<p>上で説明した、UIテスト中の判定もしているので通常の実行では、ここは実行されません。
<code>#if DEBUG</code>なんかで囲ってしまうとさらによいでしょう。</p>
<p>参考: <a href="https://github.com/fastlane/snapshot/issues/215">UI Testing: Snapshot helper clicks item on tabBar #215</a></p>
<h3 id="ok">許可を求めるダイアログでOKする</h3>
<p>コンタクト情報へのアクセスなどの許可を求めるダイアログは、アプリ内の要素なので次のようにすればOK。</p>
<pre class="brush:swift">let app = XCUIApplication()
let button = app.alerts.element.collectionViews.buttons["OK"]
if button.exists {
button.tap()
}
</pre>
<p>参考: <a href="http://stackoverflow.com/questions/31746225/xcode7-xcode-ui-tests-how-to-handle-location-service-alert/32227933#32227933">Xcode7 | Xcode UI Tests | How to handle location service alert?</a></p>
<h3 id="">大きめのデバイスでスクリーンショットが汚ない</h3>
<blockquote>
<b>2015.12.26 追記</b> このバグはFastlane 1.2.0で修正されました。起動時に自動で、シミュレータサイズが等倍になります。
</blockquote>
<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/--q-QvByje-s/VjI7GwI7pTI/AAAAAAAADi8/OVOsyumR3e4/s1600/Screen%2BShot%2B2015-10-30%2Bat%2B0.27.52.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/--q-QvByje-s/VjI7GwI7pTI/AAAAAAAADi8/OVOsyumR3e4/s400/Screen%2BShot%2B2015-10-30%2Bat%2B0.27.52.png" /></a></div>
<p>iPhone 5よりも横幅が長いデバイスではスクリーンショットが上のように汚なくなることがあります。</p>
<p>これはシミュレータのバグです。シミュレータの倍率を等倍にすると回避できます。</p>
<p>参考: <a href="https://github.com/fastlane/snapshot/issues/249">Missing UI components in the screenshot #249</a></p>
<h3 id="fastlaneandroid">fastlaneのAndroid対応</h3>
<p>次のWikiを参照してください。</p>
<ul>
<li><a href="https://github.com/fastlane/fastlane/blob/master/docs/Android.md">fastlane for Android</a></li>
</ul>
<p><code>fastlane actions --platform android</code>で確認したところ、<code>gradle</code>が追加されているようでした。
それ以外のアクションとしては、git系のツールやslackなどのサービス向けのアクションがiOSと同様に使えるみたいでした。</p>
<h2 id="">おわりに</h2>
<p>今回は株式会社はてなではなく、フリュー株式会社での開催。
フリュー様の会議室は京都タワーの地下なので、京都駅前に近くて非常に便利でした。</p>
<p>会場と食事の提供をくださったフリュー株式会社に感謝いたします。</p>
<h2 id="">関連項目</h2>
<ul>
<li><a href="https://github.com/fastlane/snapshot">fastlane/snapshot</a></li>
<li><a href="http://kanmoba.connpass.com/event/21395/">関西モバイルアプリ研究会 #7</a></li>
</ul>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-76643859798171990572015-10-14T10:00:00.000+09:002015-10-14T10:00:02.144+09:00iOSなどのAPI Diffをside-by-sideのdiffにするSafari拡張をつくった<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-7CJL6Q9B8qU/Vh0Jdwqa9YI/AAAAAAAADiY/YS9-Ql9R0uo/s1600/sample2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-7CJL6Q9B8qU/Vh0Jdwqa9YI/AAAAAAAADiY/YS9-Ql9R0uo/s640/sample2.png" /></a></div>
<p>Appleの<a href="https://developer.apple.com/library/ios/releasenotes/General/iOS90APIDiffs/index.html#//apple_ref/doc/uid/TP40016222">iOS API Diffs</a>のページの差分がわかりずらいので、それを見やすくするSafari拡張をつくりました。</p>
<ul>
<li><a href="https://github.com/safx/Safari-Extension-Swift-API-Diff">safx/Safari-Extension-Swift-API-Diff · GitHub</a></li>
</ul>
<a name='more'></a>
<p>こんな感じのiOSのAPI Diffを…
<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-0fD2cOd7wjQ/Vh0Jd6rzTPI/AAAAAAAADiU/6Eq8LdPx8_Y/s1600/orig.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-0fD2cOd7wjQ/Vh0Jd6rzTPI/AAAAAAAADiU/6Eq8LdPx8_Y/s640/orig.png" /></a></div></p>
<p>こんな風にside-by-sideのdiffにします。
<div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/-5eAuKtWaFAY/Vh0Jd4aEr_I/AAAAAAAADiQ/UAsLhUmtRd8/s1600/sample1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-5eAuKtWaFAY/Vh0Jd4aEr_I/AAAAAAAADiQ/UAsLhUmtRd8/s640/sample1.png" /></a></div></p>
<h2 id="">導入</h2>
<p>GitHubの<a href="https://github.com/safx/Safari-Extension-Swift-API-Diff/releases">リリースページ</a>からSwiftAPIDiff.safariextzをダウンロードして、それをクリックするだけです。</p>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-73977624577415205712015-10-07T10:00:00.000+09:002015-10-16T22:47:52.071+09:00GitHub + Slather + TravisCI + Codecov.ioでSwiftのコードカバレッジを取る<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUKkvn4BZ-izOELggn7yBrbA6e0AXZoZ00V0Oyf-Z5SydCEmBU-xb_xmc-6F_yW4ptI14gk800m8rr6abRFwnhWCQGQmdE9VO7HovQCqwKjJrUUxcO4immWIThJx00dYiL6Xr49W24Qfk/s1600/P9091143.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUKkvn4BZ-izOELggn7yBrbA6e0AXZoZ00V0Oyf-Z5SydCEmBU-xb_xmc-6F_yW4ptI14gk800m8rr6abRFwnhWCQGQmdE9VO7HovQCqwKjJrUUxcO4immWIThJx00dYiL6Xr49W24Qfk/s400/P9091143.jpg" /></a></div>
<p>Xcode 7からSwiftでのコードカバレッジが採れるようになりましたので、<a href="https://travis-ci.org/">Travis CI</a>上で<a href="https://github.com/venmo/slather">Slather</a>を実行することでカバレッジデータを取得して、それを<a href="https://codecov.io/">Codecov</a>に送るようにします。</p>
<p>本エントリを作成するにあたって、次の記事が大変参考になりました。</p>
<ul>
<li><a href="http://mgrebenets.github.io/mobile%20ci/2015/09/21/code-coverage-for-ios-xcode-7/">Code Coverage for iOS (Xcode 7)</a></li>
</ul>
<a name='more'></a>
<blockquote>
<b>2015.10.16追記</b>
<p>Codecov.ioはslatherなしでもカバレッジ取得が可能なので、本文中のSlatherに関する記述を削除</p>
</blockquote>
<h2 id="tldr">tl;dr</h2>
<ol>
<li><p>CodecovにGithubリポジトリを追加</p></li>
<li><p>Xcode上でテストするスキームのGather coverage dataにチェック</p></li>
<!--
<li><p>次のような<code>Gemfile</code>を追加</p>
<pre class="brush:plain">source 'https://rubygems.org'
gem 'slather', :git => "https://github.com/venmo/slather.git"
</pre></li> -->
<li><p><code>.travis.yml</code>の<code>after_success</code>セクションに次のような記述を追加</p>
<!--
- bundle exec slather coverage --input-format profdata --cobertura-xml --output-directory coverage --scheme yavfl-iOS yavfl.xcodeproj
-->
<pre class="brush:plain">
after_success:
- bash <(curl -s https://codecov.io/bash)
</pre></li>
<li><p>バッジやグラフの追加</p></li>
</ol>
<h2 id="xcodecodecoverage">XcodeのCode Coverageの設定</h2>
<p>Edit Scheme… → Test → Gather coverage dataにチェックを入れておきます。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-LUvnsNGzlQM/VhPA5OzHQgI/AAAAAAAADhY/iTTN6teb-0U/s1600/Screenshot_2015_10_03_0_05.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-LUvnsNGzlQM/VhPA5OzHQgI/AAAAAAAADhY/iTTN6teb-0U/s600/Screenshot_2015_10_03_0_05.png" /></a></div>
<p>これにチェックを入れた上で、メニュー → Edit → Show Code Coverageにもチェック入れて、Xcodeでテスト実行すると、とりあえずXcode上でカバレッジを見ることができます。</p>
<!--
<h2 id="slather">Slatherのインストール</h2>
<p>Xcodeでのカバレッジデータは、<code>~/Library/Developer/Xcode/DerivedData/<MyApp>/Build/Intermediates/CodeCoverage/<MyApp>/Coverage.profdata</code>に生成されます。</p>
<p>これは、<a href="http://llvm.org/docs/CoverageMappingFormat.html">LLVMが用いているカバレッジフォーマット</a>で、<a href="http://llvm.org/docs/CommandGuide/llvm-cov.html">llvm-cov</a>などで見ることができますが、Codecov.ioなどはこのデータ形式を直接扱ってくれません。</p>
<p>そこで、データ形式を変換するために<a href="https://github.com/venmo/slather">Slather</a>を利用します。</p>
<p>残念ながら、現時点でリリースされているslather 1.8.1ではこのデータを読むことができませんが、開発中のslatherでは大丈夫なのでこれを利用します。
開発バージョンを利用するために、次のような<code>Gemfile</code>を用意します。</p>
<pre class="brush:plain">source 'https://rubygems.org'
gem 'slather', :git => "https://github.com/venmo/slather.git"
</pre>
<p>そして、<code>.travis.yml</code>には次のような記述を追加します。</p>
<pre class="brush:plain">after_success:
- bundle exec slather coverage --input-format profdata --cobertura-xml --output-directory coverage --scheme MyScheme MyProject.xcodeproj
</pre>
<p><code>slather</code>は実際のところ、ただの変換ツールではなくて、テストを実行してカバレッジを作るまでを支援するためのツールです。
上記のように書いておくと、Travis CIのジョブが実行が成功した場合には<code>slather</code>が実行されて、次のようなログが表示されるようになります。</p>
<pre class="brush:bash">$ bundle exec slather coverage --input-format profdata --cobertura-xml --ignore "../**/*/Xcode*" --output-directory coverage --scheme DiffableArray DiffableArray.xcodeproj
Slathering...
Slathered
</pre>
<p>ちなみに、slatherとはバターなどを厚く塗るという意味の動詞らしいです。テストがカバーする部分もどんどん厚くしていきましょう。</p>
-->
<h2 id="codecov.io">Codecov.ioでのリポジトリ準備</h2>
<p><a href="https://codecov.io/">Codecov</a>側でリポジトリを扱えるようにします。必要なことはCodecovのページでAdd new repository to Codecovをクリックしてリポジトリを選ぶだけです。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-ec1jgqlXI4c/VhPA5ERMTVI/AAAAAAAADhg/AZcvIOlCivY/s1600/safx.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-ec1jgqlXI4c/VhPA5ERMTVI/AAAAAAAADhg/AZcvIOlCivY/s400/safx.png" /></a></div>
<p>リポジトリを選択すると次のようなページが表示されますが、Travisと連携する場合には何もする必要はありません。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-M_AYKZDFA3Q/VhPA5g5pPWI/AAAAAAAADhk/iDFhIgOZrPE/s1600/safx_TypetalkKit_master.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-M_AYKZDFA3Q/VhPA5g5pPWI/AAAAAAAADhk/iDFhIgOZrPE/s400/safx_TypetalkKit_master.png" /></a></div>
<h2 id="codecov.io">Codecov.ioへのガバレッジデータの送信</h2>
<p>あとは、<code>.travis.yml</code>に次のコードを追加するだけです。</p>
<pre class="brush:plain">after_success:
- bash <(curl -s https://codecov.io/bash) # この行を追加
</pre>
<p>これでTravisでビルドされたときに、成功ならカバレッジのデータがCodecovに送信されて、結果が見えるようになります。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-gx6ZOdGHL8I/VhPA5Kg-I1I/AAAAAAAADhc/wG5btLCdEQA/s1600/Screen%2BShot%2B2015-10-02%2Bat%2B0.57.46.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-gx6ZOdGHL8I/VhPA5Kg-I1I/AAAAAAAADhc/wG5btLCdEQA/s600/Screen%2BShot%2B2015-10-02%2Bat%2B0.57.46.png" /></a></div>
<h2 id="">非ガバレッジコードの指定</h2>
<p>カバレッジを無視したいソースファイルはCodecov.ioのGUIで指定することができます。
なぜか、Slatherで<code>--ignore</code>を指定しても意味がなかったので、今回はCodecov.ioで設定しました。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-mJvOxYqrEi0/VhPA59MUc4I/AAAAAAAADhs/CxuPqlPdgQU/s1600/safx_yavfl_master.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-mJvOxYqrEi0/VhPA59MUc4I/AAAAAAAADhs/CxuPqlPdgQU/s600/safx_yavfl_master.png" /></a></div>
<h2 id="readme.md">README.mdへのバッジの追加など</h2>
<p>あとは、バッジをREADMEに貼りつけたり、</p>
<pre class="brush:plain">[![codecov.io](http://codecov.io/github/safx/yavfl/coverage.svg?branch=master)](http://codecov.io/github/safx/yavfl?branch=master)
</pre>
<div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/-iofHsMd9Eng/VhPA566j1rI/AAAAAAAADho/bO8e3dhdayY/s1600/safx_yavfl.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-iofHsMd9Eng/VhPA566j1rI/AAAAAAAADho/bO8e3dhdayY/s600/safx_yavfl.png" /></a></div>
<p>GitHubやBitbucketからカバレッジが見えるように<a href="https://github.com/codecov/browser-extension#codecov-browser-extension">プラウザ拡張</a>を導入するとよいでしょう。</p>
<h2 id="">関連リンク</h2>
<ul>
<li><a href="https://github.com/venmo/slather">venmo/slather · GitHub</a></li>
<li><a href="https://codecov.io/">Codecov - Code Coverage</a></li>
<li><a href="https://travis-ci.org/">Travis CI - Test and Deploy Your Code with Confidence</a></li>
<li><a href="https://github.com/codecov/browser-extension#codecov-browser-extension">codecov/browser-extension · GitHub</a></li>
</ul>Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-2136004490916100522015-10-05T11:00:00.000+09:002015-10-05T11:00:02.883+09:00SBShortcutMenuSimulatorでシミュレータ環境でのホームスクリーン上のクイックアクションを実現する<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-CK190_EVNZ8/VhE7-eE1f-I/AAAAAAAADg4/JK_ECTzuuPE/s1600/Simulator%2BScreen%2BShot%2B2015.10.04%2B23.37.44.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-CK190_EVNZ8/VhE7-eE1f-I/AAAAAAAADg4/JK_ECTzuuPE/s500/Simulator%2BScreen%2BShot%2B2015.10.04%2B23.37.44.png" /></a></div>
<p><a href="https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Adopting3DTouchOniPhone/">Appleのドキュメント</a>にある通り、3D TouchはXcode 7.0のシミュレータではサポートされていません。</p>
<p>しかし、ホームスクリーン上のクイックアクションについては<a href="https://github.com/DeskConnect/SBShortcutMenuSimulator">SBShortcutMenuSimulator</a>で疑似的に再現できます。</p>
<p><a href="https://developer.apple.com/library/ios/samplecode/ApplicationShortcuts/Introduction/Intro.html">AppleのUIApplicationShortcutItemのサンプル</a>だと次のようになります。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-MXCS_PytyQY/VhE7-bgETMI/AAAAAAAADg8/WuKFVXiHNNk/s1600/app-shortcut.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-MXCS_PytyQY/VhE7-bgETMI/AAAAAAAADg8/WuKFVXiHNNk/s800/app-shortcut.gif" /></a></div>
<a name='more'></a>
<h2 id="">ビルド</h2>
<pre class="brush:bash">git clone https://github.com/DeskConnect/SBShortcutMenuSimulator.git
cd SBShortcutMenuSimulator
make
</pre>
<h2 id="">使いかた</h2>
<p>シミュレータが起動した状態で、SBShortcutMenuSimulatorの直下で次のコマンドを実行します。</p>
<pre class="brush:bash">xcrun simctl spawn booted launchctl debug system/com.apple.SpringBoard --environment DYLD_INSERT_LIBRARIES=$PWD/SBShortcutMenuSimulator.dylib
xcrun simctl spawn booted launchctl stop com.apple.SpringBoard
</pre>
<p>これで、TCPポート8000にアプリのバンドルIDを送信すれば、それに対応したアプリのクイックアクションが表示されます。</p>
<pre class="brush:bash">echo 'com.apple.mobilecal' | nc 127.0.0.1 8000
</pre>
<h2 id="">関連リンク</h2>
<ul>
<li><a href="https://github.com/DeskConnect/SBShortcutMenuSimulator">DeskConnect/SBShortcutMenuSimulator · GitHub</a></li>
<li><a href="https://developer.apple.com/library/ios/samplecode/ApplicationShortcuts/Introduction/Intro.html">ApplicationShortcuts: Using UIApplicationShortcutItem</a></li>
<li><a href="https://developer.apple.com/ios/3d-touch/">3D Touch - iOS - Apple Developer</a></li>
<li><a href="https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Adopting3DTouchOniPhone/">Adopting 3D Touch on iPhone: Getting Started With 3D Touch</a></li>
</ul>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-32207977133268701032015-10-05T10:00:00.000+09:002015-10-05T20:02:14.482+09:00iOS 9でアプリ間のDnDができたので、関西モバイルアプリ研究会 #6で発表してきた<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-Lhcck5fJUC0/VhEZfDXbX5I/AAAAAAAADgI/xwIMgdEM3vg/s1600/ios9-dnd_key.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-Lhcck5fJUC0/VhEZfDXbX5I/AAAAAAAADgI/xwIMgdEM3vg/s550/ios9-dnd_key.png" /></a></div>
<p><a href="https://developer.apple.com/library/prerelease/ios/documentation/WindowsViews/Conceptual/AdoptingMultitaskingOniPad/index.html">iOS 9からiPadでMultitaskingが可能になりました</a>。
そこで、アプリ間のDnDができるんじゃないかと思って試してみたらできたので、<a href="http://kanmoba.connpass.com/event/19592/">関西モバイルアプリ研究会 #6</a>で発表してきました。</p>
<p>デモアプリの実行の様子はこんな感じ。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-RszAFM_WsoI/VhEZfPdbuBI/AAAAAAAADgQ/UyQgVposz_M/s1600/ios9-dnd3.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-RszAFM_WsoI/VhEZfPdbuBI/AAAAAAAADgQ/UyQgVposz_M/s700/ios9-dnd3.gif" /></a></div>
<a name='more'></a>
<p>デモアプリはこちら。</p>
<ul>
<li><a href="https://github.com/safx/iOS9-InterApp-DnD-Demo">safx/iOS9-InterApp-DnD-Demo · GitHub</a></li>
</ul>
<p>発表資料はこちら。簡単に言うと、touchesMovedがアプリのビュー範囲外でも継続しているので、IPCでどうにかすればいいよね…という内容です。</p>
<script async class="speakerdeck-embed" data-id="1454d247692c479a84a59d50cb9a7b46" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"></script>
<h2 id="q.splitviewdnd">Q. Split view以外のときにもDnDはできるの?</h2>
<p>質疑のときには、Slide OverではPrimary-app (左のアプリ) はバックグラウンドにあるからできません…と答えていたのですが、試してみたらできました。</p>
<p>アプリ側でビュー境界の判定がおかしいのでちょっとDnDが変になってますが、これはアプリ側で調整すれば何とかなるはず。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-bjcbSDteC78/VhEqfwWyd_I/AAAAAAAADgo/viOrxh0TSSs/s1600/ios9-dnd7.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-bjcbSDteC78/VhEqfwWyd_I/AAAAAAAADgo/viOrxh0TSSs/s700/ios9-dnd7.gif" /></a></div>
<p>ドキュメントをちゃんと読むと、</p>
<ul>
<li>ディバイダーが動いたときに<code>applicationWillResignActive</code>が呼ばれる</li>
<li>ディバイダーを動かし終えたときにどちらかのアプリを完全に隠したときには、そのアプリに<code>applicationDidEnterBackground</code>が呼ばれる</li>
</ul>
<p>ということなので、Slide OverではPrimary-appには、<code>applicationWillResignActive</code>が呼ばれますがバックグラウンドには行くことはありません。</p>
<p>ちなみに実機のiPad mini 2でもちゃんとDnDできました。</p>
<p>ただし、Slide Overの場合には、次のような問題があります。</p>
<ul>
<li>Primary-app (左) からSecondary-app (右) からへのDnDはできない</li>
<li>Primary-app (左) が暗いまま</li>
</ul>
<p>Picture-in-Pictureは未確認です。</p>
<h2 id="q.dnd">Q. 画像のDnDもできるの?</h2>
<p>お互いのアプリがデータについてわかり合っていれば何でも送れる…はず。</p>
<h2 id="">おわりに</h2>
<p>全部で13人の発表がありましたが、相変らずバラエティに富んだ内容で楽しく拝見させていただきました。</p>
<p>いつも会場と食事の提供をくださる株式会社はてなに感謝いたします。</p>
<h2 id="">関連項目</h2>
<ul>
<li><a href="http://kanmoba.connpass.com/event/19592/">関西モバイルアプリ研究会 #6 - connpass</a></li>
<li><a href="https://github.com/safx/iOS9-InterApp-DnD-Demo">safx/iOS9-InterApp-DnD-Demo · GitHub</a></li>
<li><a href="https://developer.apple.com/library/prerelease/ios/documentation/WindowsViews/Conceptual/AdoptingMultitaskingOniPad/index.html">Adopting Multitasking Enhancements on iPad: Getting Oriented</a></li>
</ul>Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-44557802558496829452015-09-15T01:09:00.000+09:002015-09-15T01:09:51.746+09:00第63回 Cocoa勉強会関西でSwift中間言語について話しました<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-wots3GIK2KY/VfbpEB_7iOI/AAAAAAAADf4/SbEq1U8uVuc/s1600/Screen%2BShot%2B2015-09-15%2Bat%2B0.34.01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-wots3GIK2KY/VfbpEB_7iOI/AAAAAAAADf4/SbEq1U8uVuc/s400/Screen%2BShot%2B2015-09-15%2Bat%2B0.34.01.png" /></a></div>
<p>Cocoa勉強会関西にて、Swift中間言語 (SIL: Swift Intermediate Language) について話しました。</p>
<a name='more'></a>
<p><code>swiftc</code>でオプション<code>-emit-silgen</code>を使うと、Swiftの内部で利用されている中間言語を出力することができます。</p>
<p>この言語については公式の説明は一切ないのですが、雰囲気でなんとなく読める部分もあるので、個人的に気になった<code>lazy</code>の用いたときの中間言語などを読んでみました。</p>
<script async class="speakerdeck-embed" data-id="ee6a522bdf174dfe972e718be7a7c67f" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"></script>
<h2 id="">補足</h2>
<p><a href="https://speakerdeck.com/matuyuji/swiftzhong-jian-yan-yu-wodu-mu?slide=15">スライド15枚目</a>から19枚目までは、
@starfruits_jさんによる<a href="http://www.slideshare.net/jstarfruits/swift-3d-touch-api">今のうちに知っておきたい Swiftの高速化 + 3D Touch API</a>
のArrayが空かどうかについてのコードをSILで読むことで、それを補足する説明になっています。</p>
<h3 id="lazy">lazyについての補足</h3>
<p><a href="http://yashigani.hatenablog.com/entry/2015/07/27/100139">Lazy stored propertyについて発表しました #関モバ - yashigani?.days</a>
の追記で<code>lazy var T?</code>と<code>lazy var T!</code>で挙動が違うみたいなことを言っていたので、これをSILで確かめてみたところ、値を代入するときの挙動が異なることがわかりました。</p>
<p><a href="https://speakerdeck.com/matuyuji/swiftzhong-jian-yan-yu-wodu-mu?slide=24">スライド24</a>はSILを読んで、手動でSwiftに再変換したものです。
内部的には<code>lazy</code>は<code>Optional</code>を利用して実装しています。
<code>get</code>時の動作は同じですが、<code>set</code>の操作が大きく異なります。
<code>ImplicitlyUnwrappedOptional</code>のときは<code>nil</code>を代入しようとすると、その<code>Optional</code>値が<code>nil</code>になるため、再初期化が必要になります。</p>
<h3 id="">代入についての補足</h3>
<p>@starfruits_jさん他数名に、代入についてのスライドがなかったと突っ込みがあったので、ここで補足。</p>
<p>簡単に言うと、<code>let _ = v</code>では、SILでは<code>switch_enum</code>を利用しており、かつ<code>alloc_stack</code>を利用してないという点が速度に寄与しているように思えます。</p>
<p>元のSwiftソースが次のようだとすると、</p>
<pre class="brush:swift">func check_let(v: Int?) -> Bool {
if let _ = v {
return true
}
return false
}
func check_nil(v: Int?) -> Bool {
if v != nil {
return true
}
return false
}
</pre>
<p>これをSILに変換したコードは次のようになります。
<code>check_let</code>ははじめのブロックが<code>switch_enum</code>だけになって非常にシンプルです。</p>
<pre class="brush:plain">// Contents.check_let (Swift.Optional<Swift.Int>) -> Swift.Bool
sil hidden @_TF8Contents9check_letFGSqSi_Sb : $@convention(thin) (Optional<Int>) -> Bool {
bb0(%0 : $Optional<Int>):
debug_value %0 : $Optional<Int> // let v // id: %1
switch_enum %0 : $Optional<Int>, case #Optional.Some!enumelt.1: bb1, default bb2 // id: %2
bb1(%3 : $Int): // Preds: bb0
: // trueの処理
bb2:
: // falseの処理
bb3(%14 : $Bool): // Preds: bb1 bb2
return %14 : $Bool // id: %15
</pre>
<p><code>check_nil</code>は次のように複雑になっています。</p>
<pre class="brush:plain">// Contents.check_nil (Swift.Optional<Swift.Int>) -> Swift.Bool
sil hidden @_TF8Contents9check_nilFGSqSi_Sb : $@convention(thin) (Optional<Int>) -> Bool {
bb0(%0 : $Optional<Int>):
debug_value %0 : $Optional<Int> // let v // id: %1
// function_ref Swift.Bool._getBuiltinLogicValue (Swift.Bool)() -> Builtin.Int1
%2 = function_ref @_TFSb21_getBuiltinLogicValuefSbFT_Bi1_ : $@convention(method) (Bool) -> Builtin.Int1 // user: %14
// function_ref static Swift.!= infix <A where A: Swift.Equatable> (Swift.Optional<A>, Swift.Optional<A>) -> Swift.Bool
%3 = function_ref @_TZFSsoi2neuRq_Ss9Equatable_FTGSqq__GSqq___Sb : $@convention(thin) <τ_0_0 where τ_0_0 : Equatable> (@in Optional<τ_0_0>, @in Optional<τ_0_0>) -> Bool // user: %13
%4 = alloc_stack $Optional<Int> // users: %5, %13, %17
store %0 to %4#1 : $*Optional<Int> // id: %5
// function_ref Swift.Optional.init <A> (Swift.Optional<A>.Type)(nilLiteral : ()) -> Swift.Optional<A>
%6 = function_ref @_TFSqCurfMGSqq__FT10nilLiteralT__GSqq__ : $@convention(thin) <τ_0_0> (@out Optional<τ_0_0>, @thin Optional<τ_0_0>.Type) -> () // user: %9
%7 = metatype $@thin Optional<Int>.Type // user: %9
%8 = alloc_stack $Optional<Int> // users: %9, %10, %16
%9 = apply %6<Int>(%8#1, %7) : $@convention(thin) <τ_0_0> (@out Optional<τ_0_0>, @thin Optional<τ_0_0>.Type) -> ()
%10 = load %8#1 : $*Optional<Int> // user: %12
%11 = alloc_stack $Optional<Int> // users: %12, %13, %15
store %10 to %11#1 : $*Optional<Int> // id: %12
%13 = apply %3<Int>(%4#1, %11#1) : $@convention(thin) <τ_0_0 where τ_0_0 : Equatable> (@in Optional<τ_0_0>, @in Optional<τ_0_0>) -> Bool // user: %14
%14 = apply %2(%13) : $@convention(method) (Bool) -> Builtin.Int1 // user: %18
dealloc_stack %11#0 : $*@local_storage Optional<Int> // id: %15
dealloc_stack %8#0 : $*@local_storage Optional<Int> // id: %16
dealloc_stack %4#0 : $*@local_storage Optional<Int> // id: %17
cond_br %14, bb1, bb2 // id: %18
bb1: // Preds: bb0
:
</pre>
<h2 id="">関連項目</h2>
<ul>
<li><a href="http://safx-dev.blogspot.jp/2015/07/62-cocoametalwaifu2x.html">Safx: 第62回 Cocoa勉強会関西でMetalとwaifu2xについて話しました</a></li>
<li><a href="http://yashigani.hatenablog.com/entry/2015/07/27/100139">Lazy stored propertyについて発表しました #関モバ - yashigani?.days</a></li>
</ul>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-78274520431881727892015-08-28T01:27:00.000+09:002015-10-05T20:02:24.680+09:00Twitterロゴ入りのスピーカーをいただきました
<div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/-wss-BXsP1dA/Vd8tvpAi7dI/AAAAAAAADe8/sTq3Drz8x5w/s1600/P8271095.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-wss-BXsP1dA/Vd8tvpAi7dI/AAAAAAAADe8/sTq3Drz8x5w/s400/P8271095.jpg" /></a></div>
<p><a href="http://kanmoba.connpass.com/event/18762/">関西モバイルアプリ研究会 #5</a>で発表した全員のスピーカーにTwitterロゴ入りのスピーカーがプレゼントされ、私もいただきました。</p>
<a name='more'></a>
<p>今回は<a href="https://twitter.com/twitterjp">TwitterJP</a>の中の人でもある<a href="https://twitter.com/josolennoso">@josolennoso</a>さんと<a href="https://twitter.com/NozomuSas">@NozomuSas</a>さんがゲストとして登場し、<a href="https://get.fabric.io/">Fabric</a>のことを紹介してくださいました。
そして、このおふたりから、登壇者と参加者にTwitterロゴ入りスピーカーとTwitterステッカーが配られました。</p>
<p>スピーカーの外箱を開けたときはこんなかんじ。
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOs6AWfZj1cBZDP6lhtpcEGduyN271KCAeV9eBo6EHbeGwpzTARm3fmf43P-783Y-A8R5meT9_h6py8tTu07XwsCLtMs8bUfR6uT4ywWR75Dx7VYAViKlianOOzKSr7R48F1cvdZxtzAg/s1600/P8271094.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOs6AWfZj1cBZDP6lhtpcEGduyN271KCAeV9eBo6EHbeGwpzTARm3fmf43P-783Y-A8R5meT9_h6py8tTu07XwsCLtMs8bUfR6uT4ywWR75Dx7VYAViKlianOOzKSr7R48F1cvdZxtzAg/s400/P8271094.jpg" /></a></div></p>
<p>美しいTwitterのロゴ。
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-1Yku4YVeMSE/Vd8twhnc4tI/AAAAAAAADfE/PkbcOMmLs8c/s1600/P8271109.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-1Yku4YVeMSE/Vd8twhnc4tI/AAAAAAAADfE/PkbcOMmLs8c/s400/P8271109.jpg" /></a></div></p>
<p>バッテリー内蔵で4時間再生可能。LEDの上の穴はMic Inらしく、ハンズフリーの通話機能があったりするみたいです。
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-PQiddpp9BdQ/Vd8txbUJseI/AAAAAAAADfY/vgJkAu285LM/s1600/P8271119.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-PQiddpp9BdQ/Vd8txbUJseI/AAAAAAAADfY/vgJkAu285LM/s400/P8271119.jpg" /></a></div></p>
<p>背面はLine Inと充電用のUSB Mini。
<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-f7Xf_Y7V6eA/Vd8tv8-eS3I/AAAAAAAADe4/U0RfZW6acdI/s1600/P8271097.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-f7Xf_Y7V6eA/Vd8tv8-eS3I/AAAAAAAADe4/U0RfZW6acdI/s400/P8271097.jpg" /></a></div></p>
<p>接続例。ちなみにBluetoothでの接続も可能。ただしその場合は本スピーカーの周囲10cm以内にないと通信がうまくいきませんでした。
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhF4ucGw5PM6fk-zXgYgMQPKr7DGtp4ypGwBth_0Q8ZhUVOMpeipOjXSvhfAMWJl4IkJ5mA5AsF_tRjDnnQWExkQXXFY-I9_eOrGmuB7QMZqU5PGoukZyj_NLekBmh-msSHEvAn4R5ttwI/s1600/P8271116.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhF4ucGw5PM6fk-zXgYgMQPKr7DGtp4ypGwBth_0Q8ZhUVOMpeipOjXSvhfAMWJl4IkJ5mA5AsF_tRjDnnQWExkQXXFY-I9_eOrGmuB7QMZqU5PGoukZyj_NLekBmh-msSHEvAn4R5ttwI/s400/P8271116.jpg" /></a></div></p>
<p>台座部分がボタンになっており、本体ごと下に5秒押し込むと電源のオン/オフします。
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-O7pRwTtz1aU/Vd8twimdLxI/AAAAAAAADfQ/TwPqencYII8/s1600/P8271114.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-O7pRwTtz1aU/Vd8twimdLxI/AAAAAAAADfQ/TwPqencYII8/s400/P8271114.jpg" /></a></div></p>
<p>ちなみに発表資料はこちら。
<script async class="speakerdeck-embed" data-id="81c03ca6582c43a3a3beb112175e5dab" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"></script></p>
<p>いつも会場と食事の提供をくださる株式会社はてなに感謝いたします。</p>
<h2 id="">関連リンク</h2>
<ul>
<li><a href="http://kanmoba.connpass.com/event/18762/">関西モバイルアプリ研究会 #5</a></li>
<li><a href="https://get.fabric.io/">Fabric - Twitter’s Mobile Development Platform</a></li>
</ul>Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-81814843882856278392015-07-24T10:00:00.000+09:002015-07-24T10:00:02.297+09:00[Swift] 再帰enumで単連結リストを実装する
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-SzdIszOZZxs/VbEHHsJtm7I/AAAAAAAADeg/oREBo3T-mBQ/s1600/P2100975-2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-SzdIszOZZxs/VbEHHsJtm7I/AAAAAAAADeg/oREBo3T-mBQ/s400/P2100975-2.jpg" /></a></div>
<p>Xcode 7 beta 4からSwiftの<code>enum</code>の<code>case</code>の型に自分と同じ型を利用して、再帰構造をつくることができるようになりました。</p>
<p>これを利用して、単連結リストを実装してみました。</p>
<a name='more'></a>
<h2 id="indirectcase">indirect case</h2>
<p>Xcode 7 beta 4からSwiftに新キーワード<code>indirect</code>が追加されています。
これを利用するとAssociate Valueに自分と同じ型を指定することができます。</p>
<p>それを利用することで、次のように単連結リストをつくることができます。</p>
<pre class="brush:swift">enum List<T> {
case Nil
indirect case Cons(head: T, tail: List<T>)
}
</pre>
<p>例えば、次のようにリストをつくることができます。</p>
<pre class="brush:swift">let list = List.Cons(head: 7, tail: .Cons(head: 5, tail: .Cons(head: 8, tail: .Nil)))
// => Cons(7, List<Swift.Int>.Cons(5, List<Swift.Int>.Cons(8, List<Swift.Int>.Nil)))
</pre>
<p>このままではリストを作るのがかなり面倒だったり、表示が見づらいので、そこらを拡張していきます。</p>
<h2 id="customstringconvertible">CustomStringConvertible</h2>
<p>まず、<code>CustomStringConvertible</code>プロトコルを実装して、結果の表示がわかりやすくなるようにカスタマイズします。</p>
<pre class="brush:swift">extension List: CustomStringConvertible {
var description: String {
switch self {
case .Nil: return "[]"
case .Cons(let v): return "\(v.head):\(v.tail)"
}
}
}
</pre>
<p>これで次のように表示されるようになります。</p>
<pre class="brush:swift">print(list)
// => "7:5:8:[]"
</pre>
<h2 id="arrayliteralconvertible">ArrayLiteralConvertible</h2>
<p>続いて、配列から簡単にリストを作成できるようにします。</p>
<pre class="brush:swift">extension List {
init(_ elements: [T]) {
self = elements
.reverse()
.reduce(.Nil) { (a, e) in
return List.Cons(head: e, tail: a)
}
}
}
</pre>
<p>外部パラメタ名を<code>_</code>にしたので、パラメタ名を省略して簡単に書けるようになりました。</p>
<pre class="brush:swift">let list2 = List([9, 8, 4, 5, 6])
// => 9:8:4:5:6:[]
</pre>
<p>このままでもかなり楽になりました。配列リテラルから自動で変換させたいので、さらに<code>ArrayLiteralConvertible</code>プロトコルを実装します。</p>
<pre class="brush:swift">extension List: ArrayLiteralConvertible {
init(arrayLiteral elements: T...) {
self.init(elements)
}
}
</pre>
<p>これで配列リテラルからの自動変換が効くようになります。</p>
<pre class="brush:swift">let list3: List = [1, 5, 4, 2]
// => 1:5:4:2:[]
</pre>
<h2 id="sequencetypemapreduce">SequenceType, map, reduce</h2>
<p>最後にこの<code>List</code>型で<code>map</code>や<code>reduce</code>を使えるようにしましょう。</p>
<p>Swift 2.0からは<code>map</code>や<code>reduce</code>はプロトコル拡張として、<code>SequenceType</code>プロトコルに実装済みです。</p>
<p>よって、<code>List</code>を<code>SequenceType</code>プロトコルに適合させるだけでこれらのメソッドが利用できるようになります。</p>
<p><code>SequenceType</code>プロトコルに適合させるには、先頭から要素をひとつづつ取るようなジェネレータを得るメソッド<code>generate</code>を実装します。</p>
<pre class="brush:swift">extension List: SequenceType {
typealias Generator = ListGenerator<T>
func generate() -> Generator {
return ListGenerator(list: self)
}
}
struct ListGenerator<T>: GeneratorType {
typealias Element = T
var list: List<T>
mutating func next() -> Element? {
switch list {
case .Nil:
return .None
case .Cons(let v):
self.list = v.tail
return v.head
}
}
}
</pre>
<p>これで、<code>map</code>や<code>reduce</code>が使えるようになります。ただし、<code>map</code>で返されるものは配列になってしまいますので、必要なら<code>List</code>でラップする必要があります。</p>
<pre class="brush:swift">let mapped = List(a.map { $0 * 3 })
// => 3:15:12:6:[]
let len = list.reduce(0) { (a, e) in
return a + 1
}
// => 4
</pre>
<h2 id="">おわりに</h2>
<p>再帰<code>enum</code>の個人的な問題点は、いろいろな型を混在させられる点です。</p>
<pre class="brush:swift">let mix: List = [9, "doo", 11.5]
</pre>
<p>逆にこれを利用できれば、いろいろおもしろいことができるかもしれませんね。</p>
<h2 id="">関連リンク</h2>
<ul>
<li><a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html#//apple_ref/doc/uid/TP40014097-CH12-ID536">Recursive Enumerations</a></li>
</ul>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-7896739351554962432015-07-13T00:37:00.000+09:002015-07-13T00:37:04.473+09:00第62回 Cocoa勉強会関西でMetalとwaifu2xについて話しました
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOPy4WSbut1ZdYjRG5l2mDviDqYz5j0oN283Gd7A2rYcMPKP_WAkriHdJYz4OfSkLlUbIGtP2oSXB-xgTzJRPYXSqmv1KMb7iy4wIoMshXJa6Q7dsSzPIAcV25Q-7cYKNmpryOgQDS07c/s1600/Screen+Shot+2015-07-13+at+0.08.03.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOPy4WSbut1ZdYjRG5l2mDviDqYz5j0oN283Gd7A2rYcMPKP_WAkriHdJYz4OfSkLlUbIGtP2oSXB-xgTzJRPYXSqmv1KMb7iy4wIoMshXJa6Q7dsSzPIAcV25Q-7cYKNmpryOgQDS07c/s400/Screen+Shot+2015-07-13+at+0.08.03.png" /></a></div>
<p>発表スライドも公開しております。</p>
<a name='more'></a>
<script async class="speakerdeck-embed" data-id="e2e39b81920a422a9d4fd8fb38e4cd25" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"></script>
<p>内容は大きく、Metalの話とwaifu2xの話の2つに分かれています。
どちらもあまり詳しい分野ではなく、かつ、発表時間の関係もあって、どちらも少し浅い内容になってしまいました。</p>
<p><a href="https://github.com/safx/waifu2x-metal">waifu2x-metal</a>の速度的な点については、waitUntilCompletedを頻繁に利用するのを止めたりすれば高速化できるはずですが、そうすると、今度はGPUメモリが足りなくなるので、ちゃんと分割処理も必要になってきたりします。</p>
<p>速度が出るようにちゃんと書けば、少くとも他の実装と同等の速度は出ると思います。</p>
<ul>
<li><a href="http://safx-dev.blogspot.jp/2015/07/metalwaifu2x.html">Safx: Metalでwaifu2xを実装してみた</a></li>
<li><a href="https://github.com/safx/waifu2x-metal">safx/waifu2x-metal · GitHub</a></li>
</ul>
<p>以下、他の気になった発表を簡単にご紹介します。</p>
<h2 id="forcetouchapisinosx">Force Touch APIs in OS X</h2>
<script async class="speakerdeck-embed" data-id="01392dad61394d75883d880502cf830a" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"></script>
<p>OS X 10.10.3から導入されて、10.11からAPIも追加された<a href="https://developer.apple.com/osx/force-touch/">Force Touch</a> APIについての紹介。</p>
<p><a href="https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSButtonCell_Class/#//apple_ref/c/econst/NSMultiLevelAcceleratorButton">NSMultiLevelAcceleratorButton</a>だと感圧の最大までのクリック段階を2よりも大きく指定できるというのがおもしろいですね (ドキュメントでは1から5の値にするっぽですが)。</p>
<p>あと、<code>NSView</code>内では<code>Swift.print</code>としないと<a href="https://developer.apple.com/library/prerelease/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSView_Class/index.html#//apple_ref/occ/instm/NSView/print:"><code>print:</code></a>が呼ばれてしまうというのは新鮮でした。</p>
<h2 id="ibdesignableibinspectableui">IBDesignable / IBInspectable で UIプロトタイピンガブル</h2>
<p><iframe src="//www.slideshare.net/slideshow/embed_code/key/gQTeg2pkXGUsuh" width="425" height="355" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe> <div style="margin-bottom:5px"> <strong> <a href="//www.slideshare.net/jstarfruits/ibdesignable-ibinspectable-ui" title="IBDesignable / IBInspectable で UIプロトタイピンガブル" target="_blank">IBDesignable / IBInspectable で UIプロトタイピンガブル</a> </strong> from <strong><a href="//www.slideshare.net/jstarfruits" target="_blank">Masaki Oshikawa</a></strong> </div></p>
<p>IBDesignableを使うと便利そうなパターンについてのいろいろな紹介。</p>
<p>IBInspectableにEnumが使えないのは本当に残念ですよね。Enum値がプルダウンメニューから選択できるようになれば最高なんですが。</p>
<p>参考</p>
<ul>
<li><a href="http://safx-dev.blogspot.jp/2014/11/ibdesignableibinspectable.html">Safx: @IBDesignableと@IBInspectableでストーリーボード上に表示されるビューをつくる</a></li>
</ul>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-74021585453165565822015-07-01T10:00:00.000+09:002015-07-01T10:00:02.527+09:00Metalでwaifu2xを実装してみた<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_dO6Q2FWFwLIPDjvSDPIdL77UEKx3Vv6bZ3GBOgXJ6Ikm7B1Vg30Zab6UNZs7uur9ygrTbY2HvtDi3lik_bN0npTVCWBsvnYekhz6_6JeefunAll1VzkXKNgZCawoHfZbeBMQr1L9qPo/s1600/waifu2x.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_dO6Q2FWFwLIPDjvSDPIdL77UEKx3Vv6bZ3GBOgXJ6Ikm7B1Vg30Zab6UNZs7uur9ygrTbY2HvtDi3lik_bN0npTVCWBsvnYekhz6_6JeefunAll1VzkXKNgZCawoHfZbeBMQr1L9qPo/s400/waifu2x.png" /></a></div>
<p>Mac OS X 10.11からMetalが利用できるようになるということなので、試しにコマンドラインツールとして、<a href="http://waifu2x.udp.jp/">waifu2x</a>をMetalで実装してみました。</p>
<p>まだいろいろ問題が山積みですが、とりあえず公開します。</p>
<ul>
<li><a href="https://github.com/safx/waifu2x-metal">safx/waifu2x-metal · GitHub</a></li>
</ul>
<p>なお、コンパイルにはXcode 7、OS X上でMetalを実行するには10.11 (El Capitan)が必要です。</p>
<a name='more'></a>
<p>コンパイル環境、動作確認した環境は次の通りです。</p>
<ul>
<li>OS X 10.11 Beta2</li>
<li>Xcode 7.0 beta2 (7A121I)</li>
<li>MacBook Pro (Retina, Mid 2012)</li>
<li>NVIDIA GeForce GT 650M</li>
</ul>
<p>参考にしたソースは<a href="https://marcan.st/transf/waifu2x.py">waifu2x.py</a>と<a href="https://github.com/ueshita/waifu2x-converter-glsl">ueshita/waifu2x-converter-glsl</a>ですが、Metal版ではRGBモデルを利用しています。</p>
<p>Metalでの実装についての話は、<a href="http://cocoa-kansai.connpass.com/event/16540/">第62回 Cocoa勉強会関西</a>でする予定ですので、ここではMetal版の実装上の問題点を挙げておきます。</p>
<h2 id="">他のツールと比べて遅い</h2>
<p><a href="https://github.com/ueshita/waifu2x-converter-glsl">ueshita/waifu2x-converter-glsl</a>と比べると倍以上遅いです。</p>
<pre><code>waifu2x-metal a.jpg 0.98s user 7.84s system 11% cpu 1:18.46 total
waifu2x-converter-glsl -m scale a.jpg 1.81s user 3.20s system 15% cpu 32.47 total
</code></pre>
<h2 id="">2倍拡大にしか対応していない</h2>
<p>他ツールと比べて遅かったのもあって、コマンドライン処理あたりにはまじめに手を入れてません。
なので、ノイズ除去フィルタには対応していません。</p>
<h2 id="">分割処理していないので大きい画像は処理できない</h2>
<p><a href="https://github.com/nagadomi/waifu2x">オリジナルwaifu2x</a>や
<a href="https://github.com/ueshita/waifu2x-converter-glsl">ueshita/waifu2x-converter-glsl</a>
では入力画像を128x128ピクセルごとに分割して処理しているようですが、Metal版ではそれを行っていません。</p>
<p>そのため、処理できる画像サイズに制限があります。上限はGPUのメモリサイズによりますが、手元の環境 (GPUメモリサイズ 1GB) では元画像が800x600くらいが限界でした。</p>
<h2 id="intel">Intelで正常な結果が得られない</h2>
<div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/-zjdCsgRaqtY/VZK6Aq40lFI/AAAAAAAADd8/vxD-cxLZ6mY/s1600/waifu2x_intel.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-zjdCsgRaqtY/VZK6Aq40lFI/AAAAAAAADd8/vxD-cxLZ6mY/s400/waifu2x_intel.png" /></a></div>
<p>原因は調査中ですが、IntelのGPUを利用した場合には最終結果が上のようになってしまいます。</p>
<h2 id="">テクスチャなどのリソースの無駄使いが多い</h2>
<p>Metal Programming Guideの<a href="https://developer.apple.com/library/prerelease/mac/documentation/Miscellaneous/Conceptual/MetalProgrammingGuide/Cmd-Submiss/Cmd-Submiss.html#//apple_ref/doc/uid/TP40014221-CH3-SW1">Transient and Non-transient Objects in Metal</a>では、パフォーマンスのためにTextureは再利用すべき、とのことですが利用していません。</p>
<h2 id="">無駄な処理がある</h2>
<p>頻繁に<code>commit</code>して、さらにその直後に<a href="https://developer.apple.com/library/prerelease/ios/documentation/Metal/Reference/MTLCommandBuffer_Ref/index.html#//apple_ref/occ/intfm/MTLCommandBuffer/waitUntilCompleted"><code>waitUntilCompleted</code></a>しているので、ここらへんを減らせば少し早くなりそうです。</p>
<h2 id="">関連リンク</h2>
<ul>
<li><a href="https://github.com/safx/waifu2x-metal">safx/waifu2x-metal · GitHub</a></li>
<li><a href="https://marcan.st/transf/waifu2x.py">waifu2x.py</a></li>
<li><a href="https://github.com/nagadomi/waifu2x">nagadomi/waifu2x · GitHub</a></li>
<li><a href="https://github.com/ueshita/waifu2x-converter-glsl">ueshita/waifu2x-converter-glsl · GitHub</a></li>
<li><a href="http://kourindrug.sakura.ne.jp/waifu2x.html">waifu2xとその派生ソフト一覧</a></li>
</ul>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-16057578978642140642015-06-18T10:00:00.000+09:002015-06-18T10:00:02.093+09:00SwiftのStringInterpolationConvertibleプロトコルで文字列出力をカスタマイズする
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-iIVoy3ekP8U/VYF95RSTjGI/AAAAAAAADdY/4x44FZBvR1w/s1600/MyPlayground_playground.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-iIVoy3ekP8U/VYF95RSTjGI/AAAAAAAADdY/4x44FZBvR1w/s1600/MyPlayground_playground.png" /></a></div>
<p>プロトコル<a href="http://swiftdoc.org/protocol/StringInterpolationConvertible/">StringInterpolationConvertible</a>の使いかたがようやくわかったので簡単に紹介します。</p>
<a name='more'></a>
<h2 id="stringinterpolationconvertible">StringInterpolationConvertibleとは</h2>
<p>String Interpolationとは簡単に言うと、文字列の中の<code>\(コレ)</code>のことです。</p>
<p>つまり、<a href="http://swiftdoc.org/protocol/StringInterpolationConvertible/">StringInterpolationConvertible</a>とは、
String Interpolationを含む文字列をコンバートできるようにしたクラス、ということになります。</p>
<h2 id="stringinterpolationconvertible">StringInterpolationConvertibleプロトコルの実装例</h2>
<p>StringInterpolationConvertibleプロトコルの実装すると、次のように数値に16進数を併記させるといったことも可能になります。</p>
<pre class="brush:swift">print("\(777) is a magical number!" as HexNumberString)
//=> "777 (0x309) is a magical number!"
print("\(0xdead_beaf), \(UInt(777))" as HexNumberString)
//=> "3735928495 (0xDEADBEEF), 777"
</pre>
<p>実装は次のような感じ (解説は後ほど)。</p>
<pre class="brush:swift">final class HexNumberString : StringInterpolationConvertible, Printable {
let s: String
required init<T>(stringInterpolationSegment expr: T) {
s = "\(expr)"
}
required init(stringInterpolationSegment expr: Int) {
let hex = String(NSString(format:"%X", expr))
s = "\(expr) (0x\(hex))"
}
required init(stringInterpolation strings: HexNumberString...) {
s = "".join(strings.map { $0.s })
}
var description: String {
return s
}
}
</pre>
<h2 id="stringinterpolationconvertible">StringInterpolationConvertibleプロトコルの処理手順</h2>
<ol>
<li>与えられた文字列は、String Interpolationの部分と地の文字列とに分けられます。</li>
<li>先頭から順番に<code>init(stringInterpolationSegment)</code>が呼ばれて、それぞれのインスタンスが作成されます。</li>
<li>作成したインスタンスをまとめた配列で<code>init(stringInterpolation strings: HexNumberString...)</code>が呼ばれます</li>
</ol>
<p>例えば、<code>"abc\(1 + 30)" as HexNumberString</code>では、まず</p>
<ul>
<li><code>String</code>型の <code>"abc"</code></li>
<li>(評価後の) <code>Int</code>型の <code>31</code></li>
<li><code>String</code>型の <code>""</code></li>
</ul>
<p>の3つに分かれます。</p>
<p>1つめの<code>"abc"</code>と3つめの<code>""</code>に対しては<code>init<T>(stringInterpolationSegment: T)</code>が呼ばれますが、2つめの<code>31</code>には<code>init(stringInterpolationSegment: Int)</code>が呼ばれます。</p>
<p>これによって、3つの<code>HexNumberString</code>型のインスタンス (内部変数<code>s</code>は順番に<code>"abc"</code>, <code>"31 (0x1F)"</code>, <code>""</code>) が作成されます。</p>
<p>そして最後に、この3つのインスタンスをまとめた配列を引数として、<code>init(stringInterpolation strings: HexNumberString...)</code>が呼ばれます。</p>
<h2 id="stringinterpolationconvertible">StringInterpolationConvertibleプロトコルの実装の手引き</h2>
<p>汎用型の<code>init<T>(stringInterpolationSegment: T)</code>を実装する以外にも、
自分がカスタマイズしたい型を<code>init(stringInterpolationSegment: SomeClass)</code>として実装します。</p>
<p>上の例では16進数を表示させたい<code>Int</code>型について実装しています。</p>
<p>汎用型の<code>init<T>(stringInterpolationSegment: T)</code>は地の<code>String</code>を含むことがあるので何もしないのが無難でしょう。
同様に、<code>init<T>(stringInterpolationSegment: String)</code>では地の<code>String</code>を含むことがあるので注意です。</p>
<p><code>StringInterpolationConvertible</code>は実装クラスが<code>final</code>でないと次のようなエラーになるので<code>final</code>しておきます。</p>
<blockquote>
<p>Protocol ‘StringInterpolationConvertible’ requirement
‘init(stringInterpolation:)’ cannot be satisfied by a non-final class
(‘MySample’) because it uses ‘Self’ in a non-parameter, non-result
type position</p>
</blockquote>
<p>Printable (Swift 2ではCustomStringConvertible) を実装しないと<code>print</code>でクラス名が表示されるだけになるので実装します。</p>
<h2 id="">応用編</h2>
<p>数値をローマ数字や漢数字に変換するクラスや、次のような比較用タプルが使えるようにした<code>DiffString</code>というのも考えられます。</p>
<pre class="brush:swift">required init<Eq: Equatable>(stringInterpolationSegment expr: (Eq, Eq)) {
let cmp = expr.0 == expr.1 ? "==" : "!="
s = "\(expr.0) \(cmp) \(expr.1)"
}
print("\(1,2)" as DiffString)
//=> 1 != 2
print("\(5,5)" as DiffString)
//=> 5 == 5
</pre>
<p>他にも便利そうな応用例がありましたら教えてください。</p>
<h2 id="">関連リンク</h2>
<ul>
<li><a href="http://swiftdoc.org/protocol/StringInterpolationConvertible/">StringInterpolationConvertible — SwiftDoc.org</a></li>
<li><a href="http://stackoverflow.com/questions/29749187/is-there-an-equivalent-to-the-string-function-stringformat-using-swift-fo">Is there an equivalent to the string function String(format: …) using Swift formatting - Stack Overflow</a></li>
</ul>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-42404909439664278632015-06-07T09:00:00.000+09:002015-06-07T09:00:14.774+09:00IIJmioのクーポンスイッチAPIを利用したApple Watchアプリケーションを公開しました
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-J3yLqusPH_I/VXMHUoFeB1I/AAAAAAAADbk/og19fFla2KA/s1600/P6050897.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-J3yLqusPH_I/VXMHUoFeB1I/AAAAAAAADbk/og19fFla2KA/s600/P6050897.jpg" /></a></div>
<p>以前、<a href="http://safx-dev.blogspot.jp/2014/01/iijmio-ios-sample-app.html">ReactiveCocoaを利用したiOSサンプルアプリケーションを公開しました</a>が、それと同様のWatchサンプルアプリをGitHubで公開しました。</p>
<ul>
<li><a href="https://github.com/safx/MioDashboard-swift">safx/MioDashboard-swift · GitHub</a></li>
</ul>
<a name='more'></a>
<p>アプリ自体は非常に単純です。
iPhoneアプリではHddServiceCodeが表示される以外の実装はしていません。</p>
<p>契約ごとのクーポン残量の合計と、当日のクーポン利用状況のみが表示されます。
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-Fw44zzUAYv0/VXMHUrG4NmI/AAAAAAAADbs/3udU6Bcwy-Y/s1600/P6050898.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-Fw44zzUAYv0/VXMHUrG4NmI/AAAAAAAADbs/3udU6Bcwy-Y/s600/P6050898.jpg" /></a></div></p>
<p>項目をタップすると、その契約にひも付いている電話番号と当日のクーポン利用状況が表示されます。
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-gMxZ6nz7KGA/VXMHUp7nL7I/AAAAAAAADbo/-UFVMGd0qVg/s1600/P6050902.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-gMxZ6nz7KGA/VXMHUp7nL7I/AAAAAAAADbo/-UFVMGd0qVg/s600/P6050902.jpg" /></a></div></p>
<p>グランスでもほとんど同じ情報を表示します (ただし一つめの契約のみ)。
<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-THN2oZi-wLI/VXMHVFXMUUI/AAAAAAAADb0/2LqKV4XP_jg/s1600/P6050903.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-THN2oZi-wLI/VXMHVFXMUUI/AAAAAAAADb0/2LqKV4XP_jg/s600/P6050903.jpg" /></a></div></p>
<p>なお、Apple Watch側でブラウザを起動できませんので、あらかじめ本体アプリ側でOAuth認証を行っておく必要があります。</p>
<p>また、コンパイルして利用するにはだいたい次のようなことが必要です。</p>
<ul>
<li>IIJMioクーポンAPIを取得してコードに埋め込む</li>
<li>entitlementを自分向けに書き替える</li>
<li>署名を自分向けに書き替える</li>
</ul>
<h2 id="watch">Watchアプリの構成</h2>
<p>一般的なWatchアプリの構成は次の通りで、本アプリも同じです。</p>
<ul>
<li>本体アプリ</li>
<li>Watch Extension</li>
<li>WatchKitアプリ (ストーリーボードのみ)</li>
</ul>
<p>普通のiOSアプリにターゲットにWatchKit Appを追加すると下の2つが追加されます。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVYwrEg7SZ77iXvVaMpfu9MsrCjkeMtUNoEnWSGRiugy7BP4F_vbT8oEMzInIF487Sa9OrOwbb6fnVTJIiUgQIO9OUYbpPqpIE9aMQooqcc4878_39p1Hb6ZTIj9wOBdgXOtBG0iLmZnA/s1600/iijmio_watchapp_003.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVYwrEg7SZ77iXvVaMpfu9MsrCjkeMtUNoEnWSGRiugy7BP4F_vbT8oEMzInIF487Sa9OrOwbb6fnVTJIiUgQIO9OUYbpPqpIE9aMQooqcc4878_39p1Hb6ZTIj9wOBdgXOtBG0iLmZnA/s700/iijmio_watchapp_003.png" /></a></div>
<p>Watch Extensionには (設定次第ですが) 次のような3つのコントローラクラスが追加されます。</p>
<ul>
<li>InterfaceController (Watchアプリ)</li>
<li>NotificationController (Watch通知)</li>
<li>GlanceController (Watchグランス)</li>
</ul>
<p>これらを別々に実装していくことになります。
今回は上2つを実装して、グランスは放置しています。</p>
<h2 id="tips">Tips</h2>
<h3 id="grouplayoutvertical">Groupを縦に並べたいときはLayoutをVerticalにする</h3>
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-K81tR3jgzk4/VXMiRBySxSI/AAAAAAAADc8/EF8LnE970-o/s1600/iijmio_watchapp_005.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-K81tR3jgzk4/VXMiRBySxSI/AAAAAAAADc8/EF8LnE970-o/s700/iijmio_watchapp_005.png" /></a></div>
<p>WatchKit向けのUIではいろいろ配置に制限があったりします。</p>
<p>Groupには方向が決まっていて、デフォルトが横になっているので注意。</p>
<h3 id="tablerownsobject">TableRowにNSObject派生クラスを指定</h3>
<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-c9T6qSz7e4k/VXMiQbJiIRI/AAAAAAAADcc/Fh7VzFuWwPU/s1600/iijmio_watchapp_004.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-c9T6qSz7e4k/VXMiQbJiIRI/AAAAAAAADcc/Fh7VzFuWwPU/s700/iijmio_watchapp_004.png" /></a></div>
<p><code>TableRow</code>は<code>UITableViewCell</code>みたいなものなのですが、該当するものはWatchKitにはありません。</p>
<p>かわりに、NSObject派生クラスをつくって、それをクラス指定してやれば利用できるようになります。</p>
<pre class="brush:swift">class TableRow : NSObject {
@IBOutlet weak var hddServiceCode: WKInterfaceLabel!
@IBOutlet weak var couponTotal: WKInterfaceLabel!
@IBOutlet weak var couponUsedToday: WKInterfaceLabel!
}
</pre>
<h3 id="">セグエで呼ばれるメソッドがテーブルとそれ以外で異なる</h3>
<ul>
<li>テーブルには<code>contextForSegueWithIdentifier:inTable:rowIndex:</code></li>
<li>テーブル以外は<code>contextForSegueWithIdentifier</code></li>
</ul>
<h3 id="watch">Watch実機実行時にストーリーボードが変わってないことがある</h3>
<p>ストーリーボードの内容を更新しても、Watch側で更新されていないことがあるみたいです。</p>
<p>新しく追加したUI部品でnilエラーになっていたりするのはこれが原因です。</p>
<p>iPhone側のWatchアプリで開発アプリをuninstall → Installすればよいです。</p>
<h3 id="asignedresourcehasbeenaddedmodifiedordeleted.">実機実行前にエラー “A signed resource has been added, modified, or deleted.” が出て実行できない</h3>
<p>OS X 10.10.3, Xcode 6.3.2, CocoaPods 0.37.1の環境では、再度実行しようとすると次のようなエラーが出てしまいます。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-5N5fGVEgOVc/VXMiRccCj0I/AAAAAAAADc0/6z3BtAhJZCQ/s1600/iijmio_watchapp_007.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-5N5fGVEgOVc/VXMiRccCj0I/AAAAAAAADc0/6z3BtAhJZCQ/s700/iijmio_watchapp_007.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-iH6Q3sQfgZI/VXMiRNZ5fhI/AAAAAAAADc4/-ZDAtvBEZF8/s1600/iijmio_watchapp_006.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-iH6Q3sQfgZI/VXMiRNZ5fhI/AAAAAAAADc4/-ZDAtvBEZF8/s700/iijmio_watchapp_006.png" /></a></div>
<p>そのため、毎回実行する前に、<code>DerivedData</code>を削除する必要がありました (毎回フルコンパイルすることになるのでキつい)。</p>
<ul>
<li><a href="http://stackoverflow.com/questions/29878377/xcode-6-3-1-a-signed-resource-has-been-added-modified-or-deleted-issue">ios - Xcode 6.3.1 “A signed resource has been added, modified, or deleted” issue - Stack Overflow</a></li>
</ul>
<h3 id="app">Appアイコン</h3>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirkAslF32stkvcPSLtCPSEsz2rH_Ykk0Y9iWGv2I1ZzGLas1G8JJih2wQEOJ82rUIClCrP2C4GySldRXU9D0x21i5kS9lsCGJUagNlJqC0tLmj909lwm7oUiL0a3-GjHnApCxxu__YJdE/s1600/iijmio_watchapp_002.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirkAslF32stkvcPSLtCPSEsz2rH_Ykk0Y9iWGv2I1ZzGLas1G8JJih2wQEOJ82rUIClCrP2C4GySldRXU9D0x21i5kS9lsCGJUagNlJqC0tLmj909lwm7oUiL0a3-GjHnApCxxu__YJdE/s700/iijmio_watchapp_002.png" /></a></div>
<p>ターゲットWatchAppを追加するだけでは、WatchKit App向けのアイコンを追加することはできません。</p>
<p>アセット上でNew App Iconをして作り直すのがてっとり早いみたいです。</p>
<p>また、このアセットをのTarget MembershipにWatchKit Appを追加する必要があるかもしれません。</p>
<h3 id="keychainsharing">KeyChain Sharing</h3>
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-EFRqZrwDGn4/VXMiPyvw0nI/AAAAAAAADcU/Ps_E9ZRzqMk/s1600/iijmio_watchapp_001.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-EFRqZrwDGn4/VXMiPyvw0nI/AAAAAAAADcU/Ps_E9ZRzqMk/s700/iijmio_watchapp_001.png" /></a></div>
<p>Apple Watchでは単体でブラウザを起動できないので、Watch単体でOAuth認証はできません。</p>
<p>そこで、本体とWatchKit ExtensionでKeyChain Sharingを有効にした上で、</p>
<ol>
<li>本体アプリ側でOAuth認証で得られたアクセストークンを取得してKeyChainに入れる</li>
<li>WatchKit Extensionでそれを利用して、IIJMioのクーポンAPIにアクセス。情報を表示する</li>
</ol>
<p>というようにする必要があります。</p>
<p>なお、セキュリティ面を考慮してアクセストークンにはキーチェインを利用していますが、そういう必要がない場合には後述するNSUserDefaultsを利用してもよいでしょう。</p>
<h3 id="">本体がロックされているときにキーチェインにアクセスできない</h3>
<p>Watchアプリでは、iPhone本体がロックされているときには次のようなエラーが表示されます。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-sPqyhx0ib2w/VXMHVT5AteI/AAAAAAAADb8/Tjd5tuuYhys/s1600/P6060909.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-sPqyhx0ib2w/VXMHVT5AteI/AAAAAAAADb8/Tjd5tuuYhys/s600/P6060909.jpg" /></a></div>
<p>これは、本体がロックされているときには、Extensionがキーチェインにアクセスできないことが原因です (SecItemCopyMatchingなどで<code>errSecInteractionNotAllowed</code>が返されます)。</p>
<p>この詳細については次の記事を参照してください (この記事バックグラウンドフェッチについて書かれていますが、エクステンションでも同じことが言えます)。</p>
<ul>
<li><a href="http://wazanova.jp/items/706">Gilt: iOS7でbackground fetchを利用するとログアウトしてしまうバグへの対応 - ワザノバ | wazanova</a></li>
</ul>
<p>WatchKit Extensionがアクセストークンを取得したら、その後はずっと利用できますが、何らかのタイミングでWatchKit Extensionが終了することがあります。
そうするとトークンが失なわれて、再起動時に本体をロックされているときにはアクセストークンが得られずに、(今回のアプリだと) APIにアクセスできなくなります。</p>
<h3 id="nsuserdefaults">NSUserDefaultsで前の状態を記録</h3>
<p>KeyChainにアクセスできないときには、何も情報が表示できなくなるので、以前の情報を保存しておいてエラー時にそれを表示するようにしました。</p>
<p>情報の保存にはNSUserDefaultsを利用しました。これについては以前記事を書いたので、それを参照してください。</p>
<p><a href="http://safx-dev.blogspot.jp/2014/10/app-groupsnsuserdefaultsios.html">Safx: App GroupsとNSUserDefaultsでiOSアプリ間のデータを共有する</a></p>
<p>このアプリでは本体側から得た情報も、WatchKit Extensionで利用する予定だったので、App Groupsをオンにしていますが、現状では本体側は実装されていませんので得に意味がありません。</p>
<h2 id="">関連リンク</h2>
<ul>
<li><a href="https://github.com/safx/MioDashboard-swift">safx/MioDashboard-swift · GitHub</a></li>
<li><a href="http://safx-dev.blogspot.jp/2014/10/app-groupsnsuserdefaultsios.html">Safx: App GroupsとNSUserDefaultsでiOSアプリ間のデータを共有する</a></li>
<li><a href="http://wazanova.jp/items/706">Gilt: iOS7でbackground fetchを利用するとログアウトしてしまうバグへの対応 - ワザノバ | wazanova</a></li>
<li><a href="http://safx-dev.blogspot.jp/2014/10/iijmioapiiostoday.html">Safx: IIJmioクーポンAPIを使ったiOSのTodayウィジェットをつくってみた</a></li>
<li><a href="http://safx-dev.blogspot.jp/2014/01/iijmio.html">Safx: IIJmioのどら焼きをいただきました</a></li>
<li><a href="http://safx-dev.blogspot.jp/2014/01/iijmioapiios.html">Safx: IIJmioクーポンスイッチAPIのiOSサンプルアプリで使ったライブラリの雑感を書いてみる</a></li>
<li><a href="http://safx-dev.blogspot.jp/2014/01/iijmio-ios-sample-app.html">Safx: IIJmioのクーポンスイッチAPIを利用したiOSサンプルアプリケーションを公開しました</a></li>
</ul>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-52211273547663047112015-04-28T10:00:00.000+09:002015-04-28T23:39:56.131+09:00JavaScript for Automation (JXA) を利用してMac AppStoreからアプリケーションを自動インストールさせる<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-eetKGFLkr_0/VT5eAFT3BiI/AAAAAAAADaw/Wdn_Fyn05WM/s1600/mousepose.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-eetKGFLkr_0/VT5eAFT3BiI/AAAAAAAADaw/Wdn_Fyn05WM/s400/mousepose.png" /></a></div>
<p>OS X 10.10から<a href="https://developer.apple.com/library/mac/releasenotes/InterapplicationCommunication/RN-JavaScriptForAutomation/#//apple_ref/doc/uid/TP40014508-CH109-SW10">JavaScript for Automation (通称 JXA)</a>が導入されて、オートメーションのためのスクリプトがより書きやすくなりました。</p>
<p>そのJXAを使って、「AppStore.appを起動させてから、購入済みのアプリをInstalllさせる」というスクリプトを書いてみました。</p>
<p>その結果が次の地味なgifです。購入済みリストからMouseposéを探しだしてインストールさせています。
<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-iuR8m7f58sI/VT5eAHeYzlI/AAAAAAAADa4/bzncyJOJDX4/s1600/jxa.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-iuR8m7f58sI/VT5eAHeYzlI/AAAAAAAADa4/bzncyJOJDX4/s999/jxa.gif" /></a></div></p>
<p>JXAスクリプトとChefやAnsibleなんかを組み合わせると、自動でストアアプリも入れられるようになる…かもしれません。</p>
<a name='more'></a>
<h2 id="">前提</h2>
<ul>
<li>OS X 10.10 以降であること</li>
<li>AppStore.appが特定のApple IDでサインイン済みであること</li>
<li>Accessibilityでスクリプトを実行するアプリケーション (Terminal.appやScript Editor.appなど) に許可があること
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqjfzJVMS8R5OZN3Qe9r_gOxTTDSN059_VdbEhTZ2bvrfc49hoS2mJ3k2YQUbeCX0g6Nw-AIfEGH7_xup5HMrfrA8AeFxN2_DJOH3WCctFDSYFxCS071YTcCiqk5NUI1gUHxY-Ue55Aow/s1600/accessibility.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqjfzJVMS8R5OZN3Qe9r_gOxTTDSN059_VdbEhTZ2bvrfc49hoS2mJ3k2YQUbeCX0g6Nw-AIfEGH7_xup5HMrfrA8AeFxN2_DJOH3WCctFDSYFxCS071YTcCiqk5NUI1gUHxY-Ue55Aow/s400/accessibility.png" /></a></div></li>
</ul>
<h2 id="">インタプリタの起動</h2>
<p>導入部分は次の記事が詳しいので参考にしてください。</p>
<ul>
<li><a href="http://qiita.com/zakuroishikuro/items/1b02378bf9e940602d87">知らないうちにMacがシステム標準でJavaScriptで操作できるようになってた (JXA) - Qiita</a></li>
</ul>
<p>とりあえず今回はコンソールからインタラクティブモードで実行させます。</p>
<pre class="brush:bash">$ osascript -l JavaScript -i
</pre>
<p>インタプリタ上で次のようにするだけで、AppStoreアプリが起動します。</p>
<pre class="brush:javascript">>> Application("App Store").activate()
=> true
</pre>
<h2 id="">最小化ボタンのクリック</h2>
<p>AppStore.appでは<code>app.buttons[1]</code>が最小化ボタンのようだったので、試しにスクリプトで最小化ボタンをクリックさせてみました。</p>
<pre class="brush:javascript">>> system = Application("System Events")
=> Application("System Events")
>> app = system.processes["App Store"].windows["App Store"]
=> Application("System Events").processes.byName("App Store").windows.byName("App Store")
>> app.buttons[1].description()
=> "minimize button"
>> app.buttons[1].click()
=> Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").buttons.at(1)
</pre>
<p><code>Application('System Events')</code>はUIオートメーションのためのアプリケーションで、このアプリから他のプロセスのウィンドウを触りにいっています。</p>
<h2 id="ui">UI要素の列挙</h2>
<p><code>app.entireContents()</code>でUI要素のツリー内容がダンプされます。このコマンドで操作したい対象の見当をつけるとよいでしょう。</p>
<pre class="brush:javascript">>> app.entireContents() # ↓ 実際は改行や整列なし
=> [Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(0),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(0).groups.at(0),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(0).groups.at(0).buttons.at(0),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(0).groups.at(0).buttons.at(1),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(1),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(1).radioButtons.at(0),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(2),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(2).radioButtons.at(0),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(3),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(3).radioButtons.at(0),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(4),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(4).radioButtons.at(0),
Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(5),
:
</pre>
<p>なお、<code>properties()</code>や<code>attributes()</code>で、オブジェクトの属性などの列挙もできるようです。</p>
<ul>
<li>app.properties()</li>
<li>app.attributes()</li>
</ul>
<h2 id="purchases">Purchasesボタンのクリック</h2>
<p>上のタブから「購入済み」ボタンをクリックさせてみます。先ほどのダンプからそれっぽいものを見つけて、<code>description()</code>してみます。</p>
<pre class="brush:javascript">>> Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(4).radioButtons.at(0).description()
=> "Purchases"
</pre>
<p><code>Purchases</code>なので、これをクリックさせてみましょう。</p>
<pre class="brush:javascript">>> Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").toolbars.at(0).groups.at(4).radioButtons.at(0).click()
</pre>
<p>ちなみに、上記の式は次の式と等価です。</p>
<pre class="brush:javascript">>> app.toolbars[0].groups[4].radioButtons[0].click()
</pre>
<p>これで、「購入済み」タブに移動して、例えば次のような購入済みのアプリの一覧が表示されるはずです。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-_gaVQyV0FH4/VT5eAy6UrFI/AAAAAAAADa8/y22dXRwHTJQ/s1600/purchases.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-_gaVQyV0FH4/VT5eAy6UrFI/AAAAAAAADa8/y22dXRwHTJQ/s400/purchases.png" /></a></div>
<h2 id="install">Installボタンのクリック</h2>
<p>「購入済み」タブで、再度<code>app.entireContents()</code>してみます。</p>
<pre class="brush:javascript">>> app.entireContents()
=> [...
app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8),
app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).uiElements.byName("Mouseposé"),
app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).uiElements.byName("Mouseposé").groups.at(0),
app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).uiElements.byName("Mouseposé").groups.at(0).images.at(0),
app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).lists.at(0).groups.at(0),
app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).lists.at(0).groups.at(0).uiElements.byName("Mouseposé"),
app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).lists.at(0).groups.at(0).uiElements.byName("Mouseposé").uiElements.byName("Mouseposé"),
app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).lists.at(0).groups.at(0).uiElements.byName("Mouseposé").uiElements.byName("Mouseposé").staticTexts.byName("Mouseposé"),
app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).lists.at(0).groups.at(1),
app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).lists.at(0).groups.at(1).staticTexts.byName("Boinx Software"),
:
</pre>
<p>適当にアプリ名を取ってみましょう。</p>
<pre class="brush:javascript">>> app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).lists.at(0).groups.at(0).uiElements.name()[0]
=> "Mouseposé"
</pre>
<p>このリストから、直でボタンクリックしたかったのですが、<code>columns.at(0).uiElements.at(8)</code>あたりの要素をクリックしてもうまくいかなかったので、アプリ詳細ページに飛ばして、そこでインストールすることにしました。</p>
<pre class="brush:javascript">>> app.groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).lists.at(0).groups.at(0).uiElements.byName("Mouseposé").uiElements.byName("Mouseposé").staticTexts.byName("Mouseposé").click()
=> Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).tables.at(0).columns.at(0).uiElements.at(8).lists.at(0).groups.at(0).uiElements.byName("Mouseposé").uiElements.byName("Mouseposé").staticTexts.byName("Mouseposé")
</pre>
<p>移動後に、</p>
<pre class="brush:javascript">>> Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).groups.at(0).groups.at(0).buttons.at(0).description()
=> "Install, Mouseposé, Free"
>> Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).groups.at(0).groups.at(0).buttons.at(0).click()
=> Application("System Events").applicationProcesses.byName("App Store").windows.byName("App Store").groups.at(0).groups.at(0).scrollAreas.at(0).uiElements.at(0).groups.at(0).groups.at(0).buttons.at(0)
</pre>
<p>でインストールすることができました。</p>
<h2 id="">まとめ</h2>
<p>上記の結果をまとめて関数化してみました。</p>
<p>通信状態によって、画面遷移に時間がかかることがあるので、てきとうに<code>delay()</code>で数秒待ったりしてやって、できたスクリプトが次の通りです。</p>
<pre class="brush:javascript">function installFromAppStore(targetAppName) {
var AppStoreApp = "App Store"
var app = Application("System Events").processes[AppStoreApp].windows[AppStoreApp]
Application(AppStoreApp).activate()
delay(3)
app.toolbars[0].groups[4].radioButtons[0].click()
delay(3)
var tableRows = app.groups[0].groups[0].scrollAreas[0].uiElements[0].tables[0].columns[0].uiElements
var len = tableRows.length
for (var i = 1; i < len; ++i) {
var elem = tableRows[i].lists[0].groups[0].uiElements
var name = elem.name()[0]
if (name === targetAppName) {
elem.byName(targetAppName).uiElements.byName(targetAppName).staticTexts.byName(targetAppName).click()
delay(3)
app.groups[0].groups[0].scrollAreas[0].uiElements[0].groups[0].groups[0].buttons[0].click()
delay(3)
break
}
}
}
installFromAppStore("Mouseposé")
</pre>
<h2 id="">問題点</h2>
<p>上記のスクリプトでストアアプリをインストールすることはできましたが、今のところ次のような問題があります。</p>
<ul>
<li>現状、最大1つしかインストールできない (同時にやって大丈夫かどうか不明)</li>
<li>インストール完了を知る術がない</li>
<li>AppStoreのレイアウトが変わるたびに追従させる必要がある</li>
<li>インストール自動化したりしたら、Appleに怒られちゃうかもしれない</li>
</ul>
<h2 id="">(おまけ) アプリの検索</h2>
<p>検索フィールドの文字列を取ったり、</p>
<pre class="brush:javascript">>> app.toolbars.at(0).groups.at(6).textFields.at(0).value()
=> "foobar" // 検索フィールドにてきとうに入れてみた文字列
</pre>
<p>検索フィールドに文字列を入れたり、</p>
<pre class="brush:javascript">>> app.toolbars.at(0).groups.at(6).textFields.at(0).value = "newValue!!"
=> "newValue!!"
</pre>
<p>検索フィールドの文字列で検索できたりします。</p>
<pre class="brush:javascript">>> app.toolbars.at(0).groups.at(6).textFields.at(0).buttons.at(0).click()
</pre>
<h2 id="">関連リンク</h2>
<ul>
<li><a href="http://qiita.com/zakuroishikuro/items/1b02378bf9e940602d87">知らないうちにMacがシステム標準でJavaScriptで操作できるようになってた (JXA) - Qiita</a></li>
<li><a href="https://developer.apple.com/library/mac/releasenotes/InterapplicationCommunication/RN-JavaScriptForAutomation/#//apple_ref/doc/uid/TP40014508-CH109-SW10">JavaScript for Automation Release Notes</a></li>
<li><a href="http://www.macstories.net/tutorials/getting-started-with-javascript-for-automation-on-yosemite/">Getting Started with JavaScript for Automation on Yosemite – MacStories</a></li>
<li><a href="https://github.com/dtinth/JXA-Cookbook/wiki">Home · dtinth/JXA-Cookbook Wiki · GitHub</a></li>
<li><a href="http://qiita.com/advent-calendar/2014/jxa2013">JavaScript for OSX Automation AppleScriptの代替をJavaScriptでやるサンプル Advent Calendar 2014 - Qiita</a></li>
</ul>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-25088870966441867052015-03-03T10:00:00.000+09:002015-03-03T10:00:03.877+09:00Streaming APIに対応したTypetalkKit 0.2.6をリリースしました<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-Wiow5VAWBxo/VPR7CMoW2WI/AAAAAAAADac/lNylSnefBnk/s1600/streaming.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-Wiow5VAWBxo/VPR7CMoW2WI/AAAAAAAADac/lNylSnefBnk/s400/streaming.png" /></a></div>
<p>まだStreaming APIはベータ版みたいなので仕様が変更される可能性もありますが、
現時点でのだいたいのメッセージに対応できたのでCocoaPodsで公開します。</p>
<p>(タイトル画像に特に意味はありません)</p>
<a name='more'></a>
<h2 id="streamingapi">Streaming API</h2>
<p>TypetalkKitの<code>Client</code>クラスで、Streaming向けに追加されたメソッドは<code>streaming</code>だけです。これは引数として、イベントハンドリングするクロージャを与えます。</p>
<p>例えば、メッセージがポストされたときには<code>StreamingEvent.PostMessage</code>が送信されるので、これに対応するようにします。</p>
<pre class="brush:swift">Client.sharedClient.streaming { event in
switch event {
case .Connect : println("connected")
case .Disconnect(let err) : println("disconnected: \(err)")
case .PostMessage(let res) : self.appendNewPost(res.post!)
default: ()
}
}
</pre>
<p>イベントは28個に対応しています (WebSocketの接続・切断イベント<code>Connected</code>と<code>Disconnected</code>や未定義メッセージ<code>Unknown</code>を除く)。</p>
<pre class="brush:swift">public enum StreamingEvent {
case Connected
case Disconnected(NSError?)
case AcceptTeamInvite(AcceptTeamInviteEvent)
case AcceptTopicInvite(AcceptTopicInviteEvent)
case AddTalkPost(AddTalkPostEvent)
:
case UpdateTopic(UpdateTopicEvent)
case Unknown(String, [String:AnyObject])
}
</pre>
<p><a href="http://developer.nulab-inc.com/docs/typetalk/api/1/streaming">Typetalkのドキュメント</a>にはメッセージについての詳しい情報がさっぱりないので自力で取得できたもののみに対応しています。</p>
<p>だいたいのメッセージは適切なイベントに変換されます。
JSのソースコードを見たかぎりでは、対応できていないメッセージはresetMention、broadcastMessage、updatedApplicationだけだと思います (この3つは出しかたがわからなかった)。</p>
<p>なお、未対応のメッセージ (上記3つを含む) が来た場合には<code>StreamingEvent.UnknownEvent</code>になります。</p>
<p>具体的にどういうJSONが来るのかは<a href="https://github.com/safx/TypetalkKit/tree/master/Tests/data">テストデータのフォルダ</a>にある<code>streaming-*.json</code>を参照してください。</p>
<h2 id="">実装について</h2>
<p>内部的にはWebsocketsのSwift実装である<a href="https://github.com/daltoniam/starscream">Starscream</a>を利用しています。</p>
<p>なので、最新版のTypetalkKitでは計3つのライブラリに依存しています。</p>
<ul>
<li><a href="https://github.com/Alamofire/Alamofire">Alamofire</a></li>
<li><a href="https://github.com/isair/JSONHelper">JSONHelper</a></li>
<li><a href="https://github.com/daltoniam/starscream">Starscream</a></li>
</ul>
<p>送信されるメッセージや実装などは、<a href="https://github.com/safx/TypetalkKit/blob/master/Source/Client/Client%2BStreaming.swift">Client+Streaming.swift</a>を見てください。</p>
<p>Starscreamのおかげで、JSONの処理くらいしかやっていません。あと、<code>StreamingEvent</code>あたりのDRYじゃない感じをなんとかしたい…。</p>
<h2 id="">それ以外の修正</h2>
<p>OAuth2のトークンの更新がうまく行われていなかったのを修正したりしています。</p>
<h2 id="">関連リンク</h2>
<ul>
<li><a href="http://safx-dev.blogspot.jp/2014/10/swiftalamofiretypetalkapi.html">Safx: Swift+AlamofireでTypetalkのAPIを書いたので公開します</a></li>
<li><a href="http://nulab-inc.com/ja/blog/typetalk/opened-streaming-api-beta-version/">Streaming API のベータバージョンを公開しました - ヌーラボ</a></li>
<li><a href="https://github.com/safx/TypetalkKit">safx/TypetalkKit · GitHub</a></li>
<li><a href="https://github.com/Alamofire/Alamofire">Alamofire/Alamofire · GitHub</a></li>
<li><a href="https://github.com/isair/JSONHelper">isair/JSONHelper · GitHub</a></li>
<li><a href="https://github.com/daltoniam/starscream">daltoniam/Starscream · GitHub</a></li>
</ul>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0tag:blogger.com,1999:blog-5191809332257264217.post-18107323970758844572015-02-25T10:00:00.000+09:002015-03-01T20:18:01.241+09:00SwiftでassertとpreconditionとfatalErrorをうまく使い分ける
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8gWMgRgX3KyYjwAjWvgzVlGK5gmubdZOWxz9_MwXBY_TpZNg9cKtbpayaKXzL1KSDza6fx6w2vTZwqfzV4Qf0mML6s0yVzSeZjmuU4AMcEkplH4QU6hi5KZ_BUkLl8yzfrBUl3WtJTWU/s1600/assert_methods.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8gWMgRgX3KyYjwAjWvgzVlGK5gmubdZOWxz9_MwXBY_TpZNg9cKtbpayaKXzL1KSDza6fx6w2vTZwqfzV4Qf0mML6s0yVzSeZjmuU4AMcEkplH4QU6hi5KZ_BUkLl8yzfrBUl3WtJTWU/s500/assert_methods.png" /></a></div>
<p>Swiftにはアサーション系のメソッドとして、次の5つのメソッドがあります。</p>
<ul>
<li><a href="http://swiftdoc.org/func/assert/"><code>assert</code></a></li>
<li><a href="http://swiftdoc.org/func/assertionFailure/"><code>assertionFailure</code></a></li>
<li><a href="http://swiftdoc.org/func/precondition/"><code>precondition</code></a></li>
<li><a href="http://swiftdoc.org/func/preconditionFailure/"><code>preconditionFailure</code></a></li>
<li><a href="http://swiftdoc.org/func/fatalError/"><code>fatalError</code></a></li>
</ul>
<p>これらの違いや使い分けについて簡単に紹介します。</p>
<a name='more'></a>
<h2 id="swift3">Swiftには3つの最適化レベルがある</h2>
<p>まず、アサーション系メソッドを説明するために必要な、Swiftの最適化レベルについて簡単に説明します。</p>
<p>コマンドラインで<code>swiftc --help</code>を実行させてみるとわかる通り、Swiftコンパイラには3つの最適化レベルがあることがわかります。</p>
<pre class="brush:bash">$ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc --help
OVERVIEW: Swift compiler
(中略)
OPTIONS:
-Onone Compile without any optimization
-O Compile with optimizations
-Ounchecked Compile with optimizations and remove runtime safety checks
</pre>
<p>上記の説明の通り、<code>-Ounchecked</code>は<code>-O</code>よりアグレッシブなオプションというわけです。</p>
<p>まとめると、最適化レベルは次のようになります。</p>
<ul>
<li><code>-Onone</code>は最適化を行いません。Xcodeのデフォルト設定ではDebugビルドにこれが設定されています。</li>
<li><code>-O</code>は最適化を行います。Xcodeのデフォルト設定ではReleaseビルドにこれが設定されています。</li>
<li><code>-Ounchecked</code>は最適化を行い、さらに実行時の安全性チェックも消します。Xcodeのデフォルト設定では利用されていません。</li>
</ul>
<h2 id="">アサーション系メソッドまとめ</h2>
<p>簡単に言えば、アサーション系メソッドは次の2つの観点で分けられます。</p>
<ul>
<li>(最適化レベルによって) 実行されるかどうか</li>
<li>条件チェックがあるかどうか</li>
</ul>
<p>というわけで、各メソッドが各ビルドでチェックが有効になるかどうかをまとめた表がこちら。</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8gWMgRgX3KyYjwAjWvgzVlGK5gmubdZOWxz9_MwXBY_TpZNg9cKtbpayaKXzL1KSDza6fx6w2vTZwqfzV4Qf0mML6s0yVzSeZjmuU4AMcEkplH4QU6hi5KZ_BUkLl8yzfrBUl3WtJTWU/s1600/assert_methods.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8gWMgRgX3KyYjwAjWvgzVlGK5gmubdZOWxz9_MwXBY_TpZNg9cKtbpayaKXzL1KSDza6fx6w2vTZwqfzV4Qf0mML6s0yVzSeZjmuU4AMcEkplH4QU6hi5KZ_BUkLl8yzfrBUl3WtJTWU/s800/assert_methods.png" /></a></div>
<p>表中で○になっている部分は、実行時にチェックが有効になることを意味します。</p>
<p>例えば、<code>assert</code>はデバッグビルドでは有効になり、実行時に<code>condition</code>部分の結果が<code>false</code>ならアサーションエラーとなりますが、
リリースビルドや無チェックビルドでは無効になります (チェックが無効のときは<code>condition</code>は常に<code>true</code>になります)。</p>
<p><code>assert</code>の<code>condition</code>部分がなくて常に<code>false</code>なバージョンが右端<code>assertionFailure</code>です。
<code>fatalError</code>には常に<code>false</code>なバージョンはありません。</p>
<h2 id="">どう使い分けるか?</h2>
<p>各メソッドの機能的な意味よりもメソッド名が持つ意味を重視して、うまく使い分けるのがよさそうです。</p>
<p><code>fatalError</code>はXcodeを利用しているとコード補完などでたまにこんな感じで出されることがあります。呼ばれるはずのない未実装のメソッドや来るはずのない<code>case</code>なんかで利用するのがよさそうです。</p>
<pre class="brush:swift">required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
</pre>
<p><a href="https://github.com/search?l=Swift&q=precondition+language%3ASwift&ref=advsearch&type=Code&utf8=%E2%9C%93">GitHub内のSwiftコード</a>を見る限り、やはりメソッド先頭で事前条件に使われていることが多いみたいです (<a href="https://github.com/Carthage/Carthage/search?utf8=%E2%9C%93&q=precondition">Carthage</a>でもちゃんと使われていますね)。</p>
<p>というわけで、コードの各所でここにはこういう値しか来ないよというのを表明 (<code>assert</code>) しつつ、関数やループブロックの先頭なんかで事前条件 (<code>precondition</code>) を書き、想定外のどうしようもないエラーは<code>fatalError</code>するみたいなのが想定された使いかたかなと思います。</p>
<h2 id="">関連リンク</h2>
<ul>
<li><a href="http://swiftdoc.org/">SwiftDoc.org</a></li>
<li><a href="http://swiftdoc.org/func/assert/"><code>assert</code> - SwiftDoc.org</a></li>
<li><a href="http://swiftdoc.org/func/assertionFailure/"><code>assertionFailure</code> - SwiftDoc.org</a></li>
<li><a href="http://swiftdoc.org/func/precondition/"><code>precondition</code> - SwiftDoc.org</a></li>
<li><a href="http://swiftdoc.org/func/preconditionFailure/"><code>preconditionFailure</code> - SwiftDoc.org</a></li>
<li><a href="http://swiftdoc.org/func/fatalError/"><code>fatalError</code> - SwiftDoc.org</a></li>
</ul>
Anonymoushttp://www.blogger.com/profile/13931453776402259724noreply@blogger.com0