2015年12月17日木曜日

Xcode 7.2 の LLDB で Swiftのデバッグをするコツ

現在のXcode 7.2でSwiftを使ったiOSアプリのデバッグをするときのコツみたいなものをまとめました。将来的にはより良くなる可能性はあります。というか良くなってほしいです(´・_・`)

■LLDBはbreakした地点によって挙動が変わる

まずハマりどころがこれですが、現在のLLDBはbreakした地点で実行されていたコードがSwiftのコードかC言語系のコードかによってモードが変わります。
// Objective-C mode
(lldb) po [someObject property]

// Swift mode
(lldb) e someObject.property
Objective-Cモードの時にSwiftっぽい呼び出しをしたり、その逆をしてもまともにLLDBは動作しません。なので現在自分がどちらのモードのLLDBにいるのかを判断するのがキモになります。

ハマりどころとして、例えば実行中におもむろに停止ボタンを押してみるとか、debug viewボタンを押してみると、全部のコードがSwiftで書かれているアプリにも関わらずいきなりLLDBがObjective-Cモードで起動します。これはbreakされるのがシステムのC言語で書かれた領域だからです。

ちなみに現在のモードがどちらかを判断する方法は無いような気がします。気合と慣れで判断してください。オープンソースになりましたし、そのうち改善されると思います\(^o^)/

■SwiftモードのLLDBでprint object (po) したい

SwiftモードのLLDBのpoはかなり微妙な実装になっているので、SwiftモードでLLDBを使う場合にはpoの代わりにexprすなわちeを使うことをオススメいたします。
(lldb) e someObject
参照: http://stackoverflow.com/questions/28016227/when-debugging-swift-code-can-i-get-a-typed-reference-to-an-object-given-just-i

なんかこちらの記事ですとe -O -d run --で実行するといいよって書いてますけど何が違うのかはよくわかってません。個人的には普通のeでだいたい問題なかったです。

■SwiftモードのLLDBでポインタアドレスからオブジェクトを起こしたい

Objective-Cであれば
po 0x123456789012beef
で終わりなんですが、SwiftモードのLLDBはそこまで面倒を見てくれません。そこでunsafeBitCast()関数が便利に使えます。
(lldb) e let $v = unsafeBitCast(0x123456789012beef, MyView.self)
(lldb) e $v
これではかどりますね。

レガシーな Objective-C プロジェクトを Swift なプロジェクトに変換する

ここで言うレガシーなObjective-Cプロジェクトの定義とは
  • iOS 7時代 (Xcode 5) より前に作成されたプロジェクトである
  • Swiftのコードを一行も含んでいない
  • IOS_DEPLOYMENT_TARGETが8.0よりも小さい (7.xをサポートしている)
とします。

こんな由緒正しいiOSのプロジェクトを未だにメンテしている人もなかなかいないのかと思いますが、もしいらっしゃいましたらそんな方のためにSwiftなプロジェクトに変換していく方法をメモしておきます。

■前提条件

まずIOS_DEPLOYMENT_TARGETを8.0以上にしましょう。IOS_DEPLOYMENT_TARGETを8.0以上にすることでdynamic frameworkおよびclang moduleが使えるようになるため、Objective-CとSwiftの間の垣根が非常に低くなります。

■ケース1: 根っこはObjective-Cのまま、Swiftのファイルを追加

普通にSwiftのファイルを追加したらXcodeが上手いことしてくれます。具体的には

clang moduleを有効にしてdyldを使えるようにする設定
CLANG_ENABLE_MODULES = YES;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";

Swift化されるターゲットごとにブリッジングヘッダを作成
SWIFT_OBJC_BRIDGING_HEADER = "myapp/myapp-Bridging-Header.h";

Releaseビルド以外に対して最適化レベル設定
SWIFT_OPTIMIZATION_LEVEL = "-Onone";

が自動的に行われるので後はせいぜいブリッジングヘッダに使ってるObjective-Cのヘッダを書き込む程度ですみます。

■ケース2: 根っこからSwiftにする

「根っこからSwift」とは要するにmain.m(C言語のmain関数)を持たないアプリにしたいということですが、これも実は思ったより簡単にできます。
  • AppDelegate.swiftファイルを作成する
  • 既存のObjective-Cで書かれたAppDelegateを継承て新しくswiftなAppDelegateを作成して、@UIApplicationMainアノテーションを付ける
  • main.mを消す
参考: http://stackoverflow.com/questions/31309249/how-to-convert-objective-c-appdelegate-to-swift

たったのこれだけでプロジェクトの根っこがSwiftな状態になります。簡単ですね。

■さらにSwift化をすすめる

とりあえずAppDelegate.swiftを作ることで根っこはSwiftになるのですが、せっかくだからAppDelegateをまるごとSwiftにしてしまいたいものです。ここでもし、
  • UIWindowをAppDelegate内部でカスタマイズしている
  • UIApplicationをmain.mでカスタマイズしている
などの場合はそのままだとSwiftで対応するのがちょっと面倒です。

まずUIApplicationについてはInfo.plistのNSPrincipalClassを変更することで任意のカスタムクラスに差し替える事が可能です。参考はこちら: http://stackoverflow.com/questions/31642956/how-to-detect-all-touches-in-swift-2

UIWindowについてはMain.storyboardを使わなければ勝手にUIWindowを差し込まれることはなくなるので問題ないのですが、それでは困ると言う場合はawakeFromNibとかを実装してその中で
@UIApplicationMain
class MyAppDelegate: NSObject, UIResponder, UIApplicationDelegate {
  var window: UIWindow?
  override func awakeFromNib() {
    super.awakeFromNib()
    guard let defaultWindow = self.window else {
      fatalError("Something is wrong")
    }
    let window = MySuperDuperWindow()
    window.rootViewController = defaultWindow.rootViewController
    self.window = window
  }
}
とかすれば動くと思います。多分。

2015年12月1日火曜日

pyspa Advent Calendar 2015: 2015 GOTY for Programmers

この記事は pyspa Advent Calendar 2015: 12/1 分の記事です。先鋒を担当いたしますakisuteこと小野と申します。よろしくお願いします。なんか3年前にも先鋒をやったような・・・

さて2015年も終わりに近づいてきたということで、本来はGame of the Year for akisuteというタイトルで私的に今年最高だったゲームについて延々と無駄に語ろうかと思っていたのですけれども、Qiitaでは利用規約には一切明記がありませんがプログラマーに役立たない記事は投稿できないという制約がございまして、大変遺憾ながらタイトルを無理やりプログラマーに役立ちますように変更させていただいた次第であります。

しかしながらご安心ください。今年2015年はプログラマー向けゲームの大豊作当たり年でございます。プログラマーにこれからなりたい人向け、プログラマーに育成したいお子さん向け、ガチのプログラマー勢向けのゲームを多数ご用意させていただきましたので最後までお楽しみいただければと思います。

それではまえがきはこの程度にいたしまして・・・いよいよ各賞の発表です!

■お子さんのプログラマー教育向け賞: CodeSpells


あなたのお子さんを将来プログラマーに育てたいと考えているのであればこのようなゲームはいかがでしょうか?というわけで自分で魔法を自由にプログラミングして使えるゲーム、CodeSpellsがノミネートです。こちらのゲーム自体はかなり昔から発表されていたのですが、Steam Early Accessが始まったのが今年のようなので今年のノミネートとさせていただきました。

ゲーム自体はオープンワールドのゲームで目的等はまだ特になく開発段階という感じですが、このゲームのキモである魔法を自由にプログラミングして行使する点は現在でもバッチリ遊べます。各魔法にはイベント・アクション・それから分岐とループの制御構造が用意されていて、例えば
  • 光の玉を正面に発射する
  • if 光の玉が何かに接触する
  • 自分自身を接触地点にワープ
とか、
  • for 100回ループ
  • 炎をi*3.6度方向に発射
とかいろいろ工夫して魔法が作れるようになっています。大人が遊ぶにはちょっとパズル要素がないとか目的がないとか現段階では問題点が多いのですが、子供が想像力を働かせて遊ぶという当初の目的上であれば自由に地面を隆起させたり石を吹き飛ばしたり津波を起こしたりして遊べるのではないかなと思います!

■プログラマー初心者向け賞: Human Resource Machine


World of GooやLittle Infernoなどで有名なTomorrow Corporationの新作、Human Resource Machineが今年のプログラマー初心者向け賞にノミネートしました!このゲームはどんな命令でもただ言われたとおりに働くただのサラリーマンのおっさんに簡単な命令セットを与えて目的を達成する、つまり
おっさんをプログラミングするゲーム
です!新しいな。

Tomorrow Corporationらしい独特の世界観がただただ言われたとおりに動くおっさんを彩り、一風変わった雰囲気を味わうことができます。この世界観だけでもこのゲームをやる価値があります。

さてこのおっさんを操るプログラミング言語(仮におっさん言語と呼ぶことにする)ですが、一般的なアセンブリ言語の命令セットと同じような感じになっています。またレジスタ兼RAMの代わりとして床のカーペット上の区画を自由に使うことができるようになっていて、ちょっと工夫すればこんなふうにおっさんにバブルソートをさせる事ができます!

おっさん言語のコーディングはビジュアライズされていて、各命令ラベルをドラッグアンドドロップするだけで快適にコーディングが楽しめます。コメントラベルも手書きで好きなように書けるようになっていて、プログラミングをやったことがないカジュアル層の人でも無理なく遊べます。反面ガチ勢の人には簡単すぎるかもしれません。一応命令セット数とステップ数の最適化チャレンジが用意されているので、ガチ勢の人はそちらで楽しんでみてはいかがでしょうか?

■自動化大賞: Factorio


皆様
プログラマーにとって
最も邪悪なことは何でしょうか?

手作業です!

皆様
プログラマーにとって
最も素晴らしいことは何でしょうか?

自動化です!

自動化こそ正義!
自動化こそ未来!
今こそゲームも自動化されるべき!!

ということで今年最も自動化が進んだゲームに送られる自動化大賞には、スーパーマリオメーカーを差し置いてFactorioがノミネートされました!

Factorioについて解説する前に少し前置きをさせてください。
皆様はMinecraftというゲームをご存知でしょうか?
今やスーパーマリオに肩を並べ、Minecraft系という一大ジャンルを作り上げた超有名作だと思います。

しかしながら私このMinecraftというゲームが大嫌いでして。

例えば鉄の武器が欲しいじゃないですか。
なので一生懸命地面を掘って鉄を探して、鉄が見つかったら今度は石炭を探して、持って帰ってきて自分でかまどにぶち込んで鉄にして、今度は鉄を自分で並べて装備を作って、また鉄が足りなくなったら自分で地下に潜って掘ってって繰り返して。

めんどくせえんだよ!!!!!!!
人力手作業で作業するからスケールしねえし!!!!!
自動で掘らせろや!!!!!!!!!

って思っちゃうんですね(´・_・`)

Factorioも基本的にはMinecraftのようなゲームです。
ある宇宙飛行士がとある惑星に不時着してしまって帰れなくなっちゃうんですね。
そこで現地の素材を集めて加工してサバイバルして、最終的にはロケットを作って打ち上げて星から脱出するのが目的のゲームです。
というわけで最初は木を斧で切り倒したり、鉄鉱石をツルハシで掘ったりするんですね。

違うのはここからです。
手作業で鉄を掘るとかめんどくさいじゃないですか。そもそもロケットを作るには膨大な料の鉄が必要だというのに手で掘るとか現実的じゃないですよね。
そこで主人公は鉄を掘るためのツルハシ・・・じゃなくて、なんと自動砕石機を作るんですね。
それから自動で材料を運ぶためのベルトコンベアと、自動で材料を加工してものを作成する機械と、自動で機械に燃料や材料を投下するロボットアームを作るんですね。
あとは砕石機からベルトコンベアで材料を運んで、ロボットアームで機械に投下すれば、自分が寝てても別の所を探検しててもエイリアンと銃撃戦を繰り広げてても勝手に新しい製品がドンドン製造されていくんですね。

なんて賢いんだこの主人公は!!!!!!!!!!

って思っちゃいますよね\(^o^)/

そう、このゲームは
自動化こそが正義
スケールする生産こそが正義
生産効率の最適化のために機械の配置を試行錯誤するのが正義

まさにプログラマーのプログラマーによるプログラマーのためのゲームなのです!

こうして最初は1秒間に1個しかつくれなかった鉄が、工場生産効率の改善とスケールメリットのおかげで最後には1秒に1万個以上生産できるようになります。ヤバい。
こうなってくるともう最適化欲求がドンドン高まってきて、そのうちロケットを打ち上げることが目標だったのが、1秒間に何発ロケットを打ち上げられるかみたいなゲームになってきて、資源を惑星の片っ端から鉄道で運搬し、邪魔するエイリアンを片っ端から銃弾爆薬戦闘ロボットの波で蹂躙し森林を伐採して燃やし・・・どっちが悪役かわからなくなってきましたね(´・_・`)

さらにこのゲームはゲーム内のオブジェクトがすべてコンソールからluaスクリプトで操作ができ、luaスクリプトを書くことで自由にMODをプログラミングすることができます。そのため究極的にはパソコンの前で座って眺めているだけで自動的に主人公が工場を作ってロケットを打ち上げてゲームをクリアするMODをプログラミングすることも理論上可能なわけです!

自動化万歳!!

■GOTY for Programmers: TIS-100


しかしながらそのFactorioを差し置いて、今年のGOTY for ProgrammersはこのTIS-100で決まりです!名前からしてプログラマーっぽいこのゲーム、簡単に説明しますと
  • 主人公(=あなた)の叔父に当たる人物が最近、原因不明の死を遂げた
  • 叔父は生前プログラマーで、遺品整理していたらTIS-100という1980年代製の謎のコンピュータが出てきた
  • TIS-100の中には叔父の生前のメッセージや日記などが残っていた
  • 主人公であるあなたはTIS-100のプログラムを復活させてすべての謎を解き明かしていく
という設定になっております。地味、地味の極地ですね。プログラマー以外眼中にない、まさにプログラマーの・プログラマーによる・プログラマーのためのゲームとしか言いようがありません。

この作品の焦点となるTIS-100コンピュータですが、通常の1980年台のコンピュータとは全く異なるユニークな分散コンピューティングアーキテクチャを採用しています。
  • 各プログラムは横に4つx縦に3つ、最大12個の「ノード」と、複数の「入力ポート」「出力ポート」で構成される
  • 各ノードは最大上下左右4つの「ポート」で結ばれている
  • 各ノードにはアセンブリ言語を用いて命令を記述できる
  • 各ポート間はGo言語のチャンネルのような仕様を用いて入力・出力を行ったり、各ノード間の協調動作を行ったりすることができる。
  • 各ノードの命令はすべて並列で実行される
このアセンブリ言語とモダンな言語の特性を併せ持ったアーキテクチャがTIS-100でのプログラミングの最大の面白さです。うまくコーディングしてノード間を並列させれば実行ステップ数を劇的に減らしたり、複数ノード間でちょっとしたmap-reduceのような作りさえ実現することができます。

しかしながらですね、このTIS-100
異常なほど難しい
んです

開発者Zachtronicsの前作はSpaceChemというゲームでして、こちらのゲームも後半異常な難易度になってくるのですが、本作TIS-100も負けず劣らず難易度が凄まじいことになります。主に難易度が高くなる原因として、TIS-100コンピュータのアーキテクチャ上の絶妙な制約があげられます。例えばこんな感じです:
  • 1ノード内にたったの15行しか書けない。ちなみに空行、ラベル行、およびコメントも1行として数えられます。
  • 1行に確か20文字だか30文字前後しか書けない。ラベル名等は極端に短くする必要がある。
  • 1ノードにレジスタが2つしかなく、そのうち片方は計算レジスタとして使用できずただ値を一時保存するしかできない。さらにこの一時保存レジスタと値をやり取りするだけで追加の1命令が必要。
  • INCL/DECL命令がない。
  • 上記制約が合わさって1ノード内でループ構造を作るのが著しく難しく、汎用のどこでも通用するループ構造を構成するのが難しい。
というわけで普通の手続き的なコードを書くことができず、まさにTIS-100のためのまったく新しいコーディングの仕方を考えていく必要があってとにかく辛いです。実際私は最後までクリア出来ていません(´・_・`)

このただでさえ高い難易度に加え、さらに各問題には最適化チャレンジというものがありまして、使用したノード数・使用した行数・実行ステップ数の3つの点からプログラムの最適化を全世界のプログラマと競うことができるようになっています。おかげで世界中でドハマりするプログラマーが続出。ついにハッカーズマニュアルやらC言語製のシミュレータ、果てはWeb上で動作するTIS-100用コードエディタがまでが登場してしまいました。捗る!

ぜひ我こそはというガチ勢のプログラマの皆様、年末年始のお休みはこのTIS-100の謎を解き明かすのに使ってみてはいかがでしょうか?

それでは皆様、来年もHappy Gaming!

2015年11月29日日曜日

iOS でヒラギノフォントが明示的に指定された時に描画サイズの計算が正しくならない問題を修正する

タイトルからして出落ち感が少々ありますが・・・

iOSのフォントサイズ計算には長年修正されないバグというか仕様がございまして、「ヒラギノフォント(ヒラギノ角ゴシック、ヒラギノ明朝等)」が明示的に[UIFont fontWithName:size:]で指定されたとき、そのフォントを使ったUILabelやUITextViewなどの描画サイズの計算が正しくならない問題があります。iOS 6からiOS 9.1現在に至るまでずっとなので今後も直ることはないと思います。

詳細についてはこちらの記事が詳しいです。
http://qiita.com/yusuga/items/2be8c55ca561bba44702
一番下のリンク先の記事でも同様の問題が訴えられていまして、それぞれ対策が記載されていますので合わせてご参照ください。

でまぁ、対処法としてはいくつかあります。
  • UIControl.contentVerticalAlignmentFillにする
  • sizeThatFitsおよびintrinsicContentSizeの実装を差し替える
  • ヒラギノフォントを明示的に指定するのをやめる。システムフォントを使えばいいじゃない\(^o^)/
  • システムが提供するヒラギノフォントを使うのをやめて、モリサワさんなどからまともなヒラギノフォントを買ってきてそちらを使う

今回は私が実際に使っているsizeThatFitsの実装を差し替える方法を紹介したいと思います。といってもまぁ結構簡単です。以下の様な実装になっています。

見ての通り、フォントファミリーがヒラギノ系であったら、
  • widthはceilする
  • heightはもともとの高さにfontのdescenderをfabsしてから足したうえでceilする
だけです。簡単でしょう? Apple爆発しろ

2015年10月30日金曜日

iOS デバイス上で YouTube の動画を扱うときの制限事項などまとめ

iOSのデバイス上でYouTubeの動画を扱うときに、PC上のブラウザとは異なった制約がかかる箇所がいくつかあるのでまとめておきます。

■iOSアプリでYouTube動画を扱いたいとき

youtube/youtube-ios-player-helper を使うのがもっともよいです。こちらはYouTube公式のライブラリとなっています。具体的な実装としてはUIWebViewを使い、その上にYouTubeのiframe APIを用いてiframeのプレイヤーをロードして表示する実装になっています。その他一部のAPIについてはObjective-C経由でそのまま呼び出せるようになっていて便利です。
こちらに動画ロード時に使えるパラメータの一覧があります。
またこちらにプレイヤーのAPIの一覧があります。
以上に記載がある機能は基本的に使えますが、一部例外があります。以下で説明します。

■iOSアプリでYouTube動画を扱いたいとき(AVFoundationで)

諦めてください。
実際にはやろうと思えば実は可能ですし巷にはそういうアプリもございますが、基本的にYouTubeが許可していない動画そのもののURLを直接探してきて叩く必要がありまして権利的に大変問題がございますので、まっとうなアプリを作ろうとお考えの方は潔く諦めていただくことを強くおすすめいたします。ただリジェクトはされていないところを見るとApple的にはYouTubeの都合なんぞ知ったこっちゃないというスタンスのようです。

■YouTube動画をインラインで表示したい

普通に問題ありません。ロード時にplaysinlineパラメータを1に設定しましょう。

■YouTube動画のUIをカスタマイズしたい

諦めましょう。インライン再生でもフルスクリーン再生でも、最初から用意してある見た目を使うしかありません。ただしコントロールを表示するしない程度はカスタマイズ可能です。ロード時にcontrolsやfsパラメータを渡しましょう。

■YouTube動画の一部だけを再生したい

可能です。ロード時にstartパラメータとendパラメータを指定すればオッケーです。

■YouTube動画を複数同時再生したい

複数のYouTube動画を並べるぶんには問題ありませんが、同時に再生となるとあんまり自信がないです(実機で試せていません)。ちなみにAndroidの場合はできないそうです。

■YouTube動画をオートプレイしたい

アプリであれば可能ですが、iOSのSafariでは不可能です。実装上許可されていません。
アプリの場合はUIWebViewのallowsInlineMediaPlaybackをYESに設定すればautoplayオプションが有効になります。先ほどのyoutube-ios-player-helperはこちらのオプションが有効になっています。

■YouTube動画をプログラム的にフルスクリーンに切り替えたい

いわゆる標準のフルスクリーンプレイヤーを使うという意味では不可能です。諦めてください。
詳細はこちら。
ワークアラウンドとしては、

  • 最初からフルスクリーンで表示する(これは可能です)
  • ボタンを押したら一旦現在の動画をアンロードして、フルスクリーンとして再度一からロードする(可能ですが遅いです)
  • プレイヤー領域を司るHTMLを操作してフルスクリーンに見せかける

などがあります。

■YouTube動画をミュートしたい

不可能です。諦めてください。
詳細はこちら。
AVFoundationの機能などを使っても基本的にアプリ全体をミュートにすることはできません。AVAudioSessionのmodeをAVAudioSessionModeMeasurementにすることでアプリ全体のオーディオプレイバックを止めるという裏ワザもありますが、この方法を使うとビデオの再生まで止まってしまうので意味が無いです。
ただしデバイスのmuteスイッチの状態やデバイス全体の音量は普通に動画の音量に反映されますのでご安心ください。


2015年10月19日月曜日

AppBank GAMESを退職していました

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

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

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

2015年9月7日月曜日

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

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

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

最初にやること

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2015年7月31日金曜日

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

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

参考:



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

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



特徴:

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


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

2015年4月10日金曜日

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


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

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

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

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

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

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

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

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

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

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

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

■Glanceこそがすべて

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

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

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

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

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

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

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

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

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

■妄想とか将来の話

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

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

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

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

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

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

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

2015年3月16日月曜日

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

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

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

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

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

https://github.com/akisute/ParaMangar

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


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

2015年2月2日月曜日

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

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

方法1: 安全な方法

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

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


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

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

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

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

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

2015年1月15日木曜日

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

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

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

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

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

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

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


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

2015年1月1日木曜日

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

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

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

JSONライブラリ

です!

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

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

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

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