初級者から中級者にレベルアップするためのXcodeデバッグ術
効率よくiOSアプリ開発を行うために、効率よくデバッグを行いたいですよね。
このエントリでは「print文を書く以外デバッグの方法を知らなかったあの頃の自分」を初級者と定義して、自分がやってるデバッグ方法について書いてみます。
Xcodeデバッグ術
1. printを使わずに変数の中身を確認する
age, name, coverImage という以下の3つの変数が宣言されています。
let age = 27 let name = "Ryosuke Hiramatsu" let coverImage = UIImage(named: "sample.jpg")
これらの変数の中身をチェックしたい時、printで出力するのでも良いですが、それでは出力する値を変えたくなった時(print(age)
をprint(age*2+1)
に変更とか)に再度ビルドが必要になって時間がかかります。
printではなく、LLDBコマンドを使って確認することができます。
こんな感じで Breakpoint を貼って*1、そこで処理を止めた状態で Console に以下のように入力します。
(lldb) po age 27 (lldb) po name "Ryosuke Hiramatsu"
単純に変数の値を出力するだけではなく、式の評価もできます。
(lldb) po age+1 28 (lldb) po name.characters.split(" ").map{ String($0) }.first "Ryosuke"
2. QuickLookを使ってUIImageの中身を確認する
coverImage 変数に対して、printやpoコマンドではどんな画像なのか確認できません。
let coverImage = UIImage(named: "sample.jpg")
// print(coverImage) とした時のコンソール出力 Optional(<UIImage: 0x14fe19460>, {640, 640})
こういった場合、変数の上にマウスカーソルを合わせ、
現れたポップアップの目のアイコンをタップすることで画像が確認できます。
変数の値や画像の表示は、Xcodeの左下のAreaからも可能
変数の確認や QuickLook による画像の確認は Console の左のエリアからも可能です。
age や name などの変数名の横に値が出ていて一目で確認できます。
coverImage を選択した状態で目のアイコンをクリックして画像を表示することも可能です。
単純な変数の確認であればこちらの方が便利ですね。
3. 自作クラスのdescriptionをカスタマイズする
例えば以下のようなクラスを作ったとします。
class Person { let name: String let age: Int let coverImage: UIImage? init(name: String, age: Int, coverImage: UIImage?) { self.name = name self.age = age self.coverImage = coverImage } }
以下のようにPersonクラスのインスタンスを生成します。
let person = Person(name: name, age: age, coverImage: coverImage)
このインスタンスのプロパティを確認しようと思ってprint(person)
としてみると、Consoleには以下のように表示されます。
// print(person) XcodeDebugPractice.Person
これは知りたい情報ではありません。LLDBでpo person.name
などとすれば確認はできますが、少し手間がかかります。
こういうシーンではクラスのログ出力をカスタマイズしましょう。
CustomStringConvertibleを使う
先ほどの Person クラスを編集して、以下のようにします。
class Person: CustomStringConvertible { //★ let name: String let age: Int let coverImage: UIImage? init(name: String, age: Int, coverImage: UIImage?) { self.name = name self.age = age self.coverImage = coverImage } //★ var description: String { return "\(name), \(age)" } }
この状態で再びprint(person)
するとコンソールには以下が出力されます。
Ryosuke Hiramatsu, 27
description で指定した値が出ています。CustomStringConvertible
は print などで出力される値をカスタムするために用意されている Protocol です。
さらに、CustomStringDebugConvertible Protocol にも準拠してみます。
class Person: CustomStringConvertible, CustomDebugStringConvertible { // ★ // ... 省略 ... var description: String { return "\(name), \(age)" } var debugDescription: String { // ★ return "\(name), \(age), \(coverImage)" } }
CustomStringDebugConvertible はpo
コマンドなどでデバッグプリントする際の値をカスタムできるものです。
他にも CustomReflectable や CustomLeafReflectable、CustomPlaygroundQuickLookable などが用意されていますので興味のある方は調べてみてください。
4. Breakpointをカスタマイズする
Breakpoint をもっと便利に使いましょう。例えば Loop で特定の条件の時だけ止めたかったり、あるいは処理は止めずに Breakpoint を通過したら Console にログを吐きたい、というような場面があるとします。そんな時には Breakpoint のカスタマイズが便利です。
例えばこんな感じのループ文を用意して試してみます。
for i in 0...50 { print(i) }
ブレイクポイントを右クリックして「Edit Breakpoint」を選択し、
condition
に条件を入れます。今回は「i==22の時のみ止まる」ように設定してます。
この状態で実行すると、i==22のループの時のみ Breakpoint で処理が止まります。特定条件の時に変数の値を確認したい場合などに便利ですね。
おまけ:ブレイク時に音を出してデバッグする
Breakpoint では「ブレイク時にどんなOutputをするか」もカスタム可能です。
例えば「i==22の時だけ変数の値を確認したい。でも処理は止めずに続けたくて、値の内容は音声で確認したい」という場合は以下のように設定します。
これで実行すると「22 hit!」という音声が(おそらくsayコマンドで)発声され、下の「Automatically continue after evaluating actions」にチェックをつけているため処理は止まらずに継続されます。
いろんな特殊条件で発生する不具合を調査する際など、Condition をうまく使うことでデバッグを効率的に行うことができます。ちなみに自分は実プロジェクトで音声デバッグを使ったことはありませんw
5. アプリ内に保存したデータを確認する
NSUserDefaults や Document ディレクトリなどに保存したデータの中身を確認する方法です。
例えば以下のコードで値や画像を保存したとします:
// NSUserDefaultsに値を保存 let defaults = NSUserDefaults.standardUserDefaults() defaults.setObject("@himara2", forKey: "account") defaults.synchronize() // Diskに画像を保存 if let path = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .AllDomainsMask, true).first, coverImage = coverImage { let targetPath = path.stringByAppendingString("/sample_data.jpg") if let data = UIImageJPEGRepresentation(coverImage, 0.5) { let success = data.writeToFile(targetPath, atomically: true) print(success) } }
保存されたデータを確認してみます。
Xcode > Window > Organizer と選択して Organizer を開きます。
左メニューでアプリを実行したデバイスを選択して、対象のアプリを選択します。
設定ボタンから「Download Container...」を選択し、適当な場所に保存します。
xcappdata 拡張子のファイルが保存されますので、右クリックして「パッケージの内容を表示」します。
中身はこのようになっています。
Documents/ に保存した画像ファイル(sample_data.jpg)が存在しているのが分かります。
さらに、Library/Preferences/ 下のplistの中身をみると、
このように NSUserDefaults の値が保存されていることが分かります。
保存した内容を取り出すコードを実装するのでも良いのですが、ハマった時は原因が切り分けが大事なので、実際に保存されている情報を目視できるのは意味のあることだと思っています。
まとめ
Xcodeのデバッグ手法に関する記事は以外と少なく、自分が最初にアプリ開発を始めた時は NSLog を大量に埋めて頑張ってました。
今回紹介したようなデバッグ手法を知ってからスピーディに開発できるようになったな、と思ったのでこのような記事を書かせていただきました。
何か間違ってる部分や、他に良いデバッグ方法などご存知でしたらぜひ教えてください :)
みなさんのデバッグライフに少しでもお役立ちできれば光栄です。
サンプルコード
今回引用したコードはすべてこちらでお試しいただけます。
こちらもいかがですか?
*1:Breakpointは行番号のあたりをクリックで設定/解除できます
XcodeのPlaygroundをつかってUIの実装をサクサク試す
2018/04/10 追記
一部コードが古くなっています。
Xcode 9.3, Swift 4.1 の環境では以下のコードで動作します。
import PlaygroundSupport let vc = UIViewController() vc.view.frame = CGRect(x: 0, y: 0, width: 320, height: 480) vc.view.backgroundColor = .lightGray PlaygroundPage.current.needsIndefiniteExecution = true PlaygroundPage.current.liveView = vc.view
Xcode の Playground を久しぶりに使ってみて、UIの実装とかこれでやると便利だな、と思ったのでやり方をまとめてみます。
やりたい完成系はこんな感じ:
左に書いた TableView のコードが右で即時反映されてます。
前置き:そもそもPlaygroundとは
Playground というのは Xcode 6 から登場した Swift の REPL 環境です。
上記の画像のように、コードを書くと実行結果がビルドすることなしに即時に表示されます。Swift の文法を試せすのにもってこいで、自分も入門したての頃にはよく使ってました。
Apple の公式の Swift ドキュメント「The Swift Programming Language」の概要部分ではドキュメントが Playground として実装されていて、実際に値を変えたり動かしながら確認することができます。(ここのページの Download Playground から落とせます)
実際に動かせるドキュメントとして Playground はとても優秀で、読んで→書いてとやってたのが同時に可能です。最近流行りのOSSライブラリであるRxSwiftでもPlayground形式のドキュメントが同梱されたりしています。
PlaygroundでUIをつくる
3月頭に try! Swift というSwiftのカンファレンスに参加したんですが、そこで驚いたのが海外のスピーカーの方がよく Playground を使ってることでした。
文法のデモはもちろん、アニメーションの調整(モンスターボールを投げると回転して爆発してポケモンが登場する)や TableView の実装まで、Project ではなくて Playground でサクサクと進めてました。
自分は最近はほとんど Playground 使ってなくて、どれくらい便利なのかなーと思って試してみたやり方と感想をまとめます。
XCPlaygroundをimportする
下記のように記述して、XCPlayground を import します。
import XCPlayground
ライブ表示するviewを指定する
次に右側にライブ表示する view を指定します。
今回は UITableViewController を root とした NavigationController を表示するので、以下のように指定します。
let tableVC = UITableViewController() let navigationController = UINavigationController(rootViewController: tableVC) XCPlaygroundPage.currentPage.needsIndefiniteExecution = true XCPlaygroundPage.currentPage.liveView = navigationController.view
Assistant Editorを開く
viewはREPLで表示されるコンソールではなく、Assistant Editor で開いた Timeline に表示されます。
ここまでで view が表示できてるはずです。
実装を書いていく
試した内容としては、カスタムな UITableView を書いて、その view を Playground でライブ表示していきました。
Playground につらつらと書いていって、以下のように記述しました。
import UIKit final class MyTableViewController: UITableViewController { // ... MyTableViewControllerの実装をここに } let tableVC = MyTableViewController(style: .Plain, items: people, cellStyle: .Subtitle, editable: true) { cell, item in cell.textLabel?.text = item.name cell.detailTextLabel?.text = item.city } tableVC.title = "Person" let navigationController = UINavigationController(rootViewController: tableVC) navigationController.view.frame = CGRectMake(0, 0, 320, 480) import XCPlayground XCPlaygroundPage.currentPage.needsIndefiniteExecution = true XCPlaygroundPage.currentPage.liveView = navigationController.view
こんな感じで書いていくと、以下のように表示されます。
いい感じですね!
コードを修正するたびに変更が右側のViewに反映されていくので、とても効率よく実装ができます。
サンプルコード
今回試した内容はこちらのリポジトリにまとめています。
(Xcode 7.2, Swift 2.1 でつくりました)
まとめ
Playground で UI を実装するのはとても快適でした。ビルド時間はいらないし、「ビルドするぞ」と切り替える回数が少ないのも捗ります。
現在はタッチイベントなどは検知できませんが、次の Xcode のバージョンでは UITapGestureRecognizer などにも対応するようで、それができると一気に可能性が広がる気がします。
一方で、storyboard とか xib を使っての実装とは相性はよくありません。try! Swiftのスピーカー陣は storyboard 使わない派の人が多かったので、ここは気にしてないのかもしれませんね。
自分は storyboard 積極的に使ってく派ですが、アニメーションや View の部品をコードだけで実装するケースでは今後 Playground で作ろうと思いました。
Xcode Playground は文法の確認やインタラクティブなドキュメントだけでなく、UI の実装にも便利そうという話でした。 みなさまの Playground Life のお助けになれば光栄です :)
try! Swiftで特に復習したいセッションまとめ #tryswiftconf
3月2-4日の3日間、try! Swift に参加してきました。
#tryswiftconf 💖 pic.twitter.com/4jtb6XEF1J
— try! Swift Tokyo (@tryswiftconf) 2016年3月4日
try! Swift とは東京渋谷で開催された、世界中から集まった Swift デベロッパーが知識や技術を共有することを目的としたカンファレンスです。
セッションの内容は自分にとって難しいものが多く、明日から使える tips & tricks みたいなのは少なくて、プログラミング言語の中でSwiftはこういう特性があるよ、そしてそれを活かしてこんなこともできるよね!みたいな、Swiftそのものの話や言語のポテンシャルに言及するものが多かった気がします。
振り返り勉強したいセッション
一度聞いただけでは全然理解できてない
そんな濃いセッションだったので、一度聞いただけでは2割も理解できてないです。
そこで、個人的に振り返って勉強したい順にセッションをまとめてみます。
業務やプライベートの実際のプロダクトで試すのが一番理解が進められると思ったので、そういう意味での試しやすい順番に並べました。
1.既存のコードをもっとSwiftyにする系セッション
会社ではフルSwiftで書いてるけど、まだまだ言語の特性を活かせる箇所が多く見つかったので。
Blending Cultures
- オブジェクト指向, 関数型, Protocol指向の考えをミックスしてSwiftyなコードを書く
- 実際のコードベースでプレゼンが進められたので理解しやすく、抽象化の勉強になる
- 参考リンク
Table View Controllers in Swift
- TableViewController のコードを Swifty に、 Generic にする
- その TableVC に Configuration を渡すだけでコンテンツの表示がなされる
- Chris さんにはQAセッションでいくつか質問したので、その内容含めて復習する
- 参考リンク
Hipster Swift
- NSHipster の中の方による Swift の tips集
@autoclosure
やlazy var
など、理解があいまいなのでこれを機にちゃんと理解する- 参考リンク
2.デバッグやテストをしっかり学ぶ系セッション
特にテストはいまあまり書けてないので重点的に復習したい。
Swift compiler integration in LLDB
- LLDB をつかってデバッグをもっと効率よく
p
やpo
コマンドの他、CustomDebugConvertible
など- 参考リンク
An Artsy Testing Tour
- 4つのアプリを題材に、テストをどう書いていこうか?という話
- いま書いてるアプリはほぼテストないので、復習してから計画的に書く
- 参考リンク
Protocol-Oriented Programming in Networking
- APIKit のようなインターフェースをいかに実装するか
- RxSwift をつかったコードの書き方
- APIKit, RxSwift ともに業務で書いてるアプリに導入したいのでちゃんと理解する
- ishkawaさんの発表はいつも大変わかりやすく、発表スタイルも勉強になります
- 参考リンク
3.ワンランク上にレベルアップできそうなセッション
普段コードを書くときに心がけることで、メンバーや未来の自分に意図が"伝わる"コードを書ける。
The Design of Everyday Swift
- 「誰のためのデザイン?」という本の考えを Swift にあてはめて考える
- private/public/internalや、メソッド名のつけかたなどで意思の伝わるコードを書く
- 意識してるつもりではあるけど、自分のコードを見直す
- 参考リンク
本も読んでみる。
Learning to Read Again
- コードを読むときに人はどう読んでいくのか?
- 読みやすく、かつ理解されやすいコードを書くために意識することを理解する
- 参考リンク
4.OSSを公開/貢献する系のセッション
今年は自分の使ってるモジュールを切り出して公開していこうと思っているので。
Creating a Library
- ライブラリをつくって CocoaPods & Carthage & Swift Package Manager で配布する話
- 参考リンク
Motivation based library abstraction
- 日常のコードから、どうやってどのような粒度で切り離してOSS化しているかの話
- 参考リンク
Contributing to open source Swift
- OSSのなったSwift、その概要を理解し、自分が貢献できそうな領域を見つけよう、という話
- 前半の解説部分、後半の貢献へのスタンスとコミュニティの話、どちらも最高でした
- 自分でOSSをいくつか公開した後、Swift のコードをよく読んでみたい
- 参考リンク
感想・全セッションを通じて思ったこと
- サーバーサイド Swift の話がよく出ていくる
- 日本ではそんなにまだ聞かないな?と思ってたので驚いた
- みんな簡単なコードやプロトタイプくらいは Playground で書いている
- 自分も真似してちょっと使ってみたけど大変便利。今後も使っていきたい
- テストについてはみんな悩んでいる
- Protocol Extension, Functional Programming が頻出
- 意識して使えてなくて、Objective-Cy なコードを書いてしまってるなぁ、と反省
- 本質的な話が多かった
- こんなライブラリがありまして、みたいな話はほぼなくて、すべてに共通する考え方・実装の仕方の話が多かった
- 「明日からこのライブラリ使うぞ!」とはならず、「自分の普段書いてるコードはどうなのか」と振り返る機会になった
まとめ
3日間とても熱く濃い時間をすごせました。
普段ほとんど Swift を書いてるのに理解できない話がとても多く、悔しい思いもしましたが、これをエネルギーにしてひとつずつキャッチアップしていこうと思います。
スピーカーのみなさま、運営スタッフのみなさま、そして素晴らしい会場を提供してくださったサイバーエージェントさん、本当にありがとうございました!
第26回はSupershipで開催! #potatotips で発表されたiOSのtipsまとめ
約1年ぶりに #potatotips に参加させていただきました。
第26回目となる今回は Supership株式会社 さん主催での開催でした。
potatotips は1人5分の持ち時間でtipsを共有する勉強会です。
元々はクックパッドさんが始めた勉強会ですが、現在は様々な会社にて開催されています。
第26回で発表された8つのiOSのtipsをまとめます!
続きを読むiOSアプリの開発効率をあげるSwiftの便利Extension
Swift の Extension を使うと既存クラスや自作クラスを拡張することができます。
会社や個人のプロジェクトでいつも使っている便利な拡張コードをまとめてみます。