久しぶりにcocoapodsを使ってはまったところ
cocoapods 1.0.1
The dependency xxx
is not used in any concrete target.
Podfileの書き方がcocoapods 1.0系になって変わったらしく
Podfileを
$ pod init
で生成すると
# Uncomment this line to define a global platform for your project # platform :ios, '9.0' target 'SampleProj' do # Comment this line if you're not using Swift and don't want to use dynamic frameworks use_frameworks! # Pods for SampleProj pod 'Bond', '~> 4.0' target 'SampleProjTests' do inherit! :search_paths # Pods for testing end end
このようにプロジェクト名でテンプレが生成されるので、Pods for xxx以下にpod xxxを書けば良い
Unable to find a specification for 'xxxxx'
$ pod setup
してから
$ pod install
で解決
Crashlyticsにクラッシュレポートが送信されない
普通はFabricで手順通りにやってればクラッシュレポートは送信されるはずなんですが、なぜか送信されないってことがありました。
answersとかは動いているのでFabric自体はちゃんと取り込まれてるのになぜだ・・と思って調べたら
troubleshootingに書いてありました https://docs.fabric.io/ios/crashlytics/crashlytics.html#troubleshooting
Make sure our SDK line is after all other 3rd-party SDK lines that install an exception handler. (We need to be last one called in your appDidFinishLaunchingWithOptions method.)
他に例外ハンドラを使うようなライブラリ使ってる場合は、appDidFinishLaunchingWithOptionsの一番最後でFabric.with([Crashlytics()])呼べってことでした。
なぜかとういうと、クラッシュレポートを送信するライブラリはたぶんNSSetUncaughtExceptionHandlerを使って例外をキャッチしてクラッシュチェックをしているのですが、この関数は一つしかハンドラをセットできないので、複数クラッシュレポートを送るようなライブラリを使っていた場合、最後にセットしたものが動くってことだと思われます。 https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/#//apple_ref/c/func/NSSetUncaughtExceptionHandler
おまけ
どうしてもFabricを使いつつ他の例外ハンドラを動かしたい場合は、こんな感じでCrashlyticsのセットした例外ハンドラを保存しておいて、NSSetUncaughtExceptionHandlerで独自のコールバックをセットしてその中で、Crashlyticsのハンドラの実行と独自の処理をやればいいのではないかと思います(試してないけど
static var exceptionHandler: ((NSException) -> Void)? func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { Fabric.with([Crashlytics()]) AppDelegate.exceptionHandler = NSGetUncaughtExceptionHandler(); let callback: @convention(c) (NSException) -> Void = { // do somithing AppDelegate.exceptionHandler?($0) } NSSetUncaughtExceptionHandler(callback) return true }
2015振り返り
運動
いつも3ヶ月くらいで飽きてましたが、今年は週1走ることを目標にやってみてだいたい達成できました。
ブログ
ここ2年くらいさぼてったので、今年は月1くらいで継続的に書くことはできました
2015-01-01から1年間の記事一覧 - Pragmatic ball boy
会社のエンジニアブログでも初めて投稿し、100近くはてぶ獲得できました
64bit環境におけるObjective-Cのポインタ | GREE Engineers' Blog
仕事
現職に入社以来ずっと同じ仕事ばっかりだったので、社内の新規事業開発でエンジニアを募集していたので挙手してジョインしてLIMIAというサービスをリリースしました(といっても僕はほぼ実装してないですが・・
まとめ
- 飽きっぽい人でも目標があると続くのでやっぱり目標大事ですよね
2016は個人でなにかリリースしたいとおもいます
良いお年を!
コード行数の測定
Swiftでコード行数を測ろうとツールを探していたらclocというのがよさげでした
インストール
インストール方法は思ったよりたくさん用意されていてlinux系だけでなく、node.jsやhomebrew, macports用のも用意されていて、WindowsやMac環境でも楽にインストールできます
npm install -g cloc # https://www.npmjs.com/package/cloc sudo apt-get install cloc # Debian, Ubuntu sudo yum install cloc # Red Hat, Fedora sudo pacman -S cloc # Arch sudo pkg install cloc # FreeBSD sudo port install cloc # Mac OS X with MacPorts brew install cloc # Mac OS X with Homebrew
使い方
基本的にはclocのあとに対象のディレクトリを指定すれば、そのディレクトリ以下を含めてコードを解析してくれます
実行
$ cloc Hoge
実行結果
1572 text files. 1417 unique files. 684 files ignored. http://cloc.sourceforge.net v 1.64 T=19.50 s (46.0 files/s, 9700.3 lines/s) ------------------------------------------------------------------------------- Language files blank comment code ------------------------------------------------------------------------------- C 92 5068 5097 39838 Bourne Shell 15 5644 7198 34171 Swift 365 4776 4414 24868 m4 8 1074 133 9969 C/C++ Header 253 4803 14036 8319 Objective C 43 1790 721 7418 make 20 845 547 6923 YAML 17 11 0 410 JSON 14 0 0 360 D 64 0 0 242 Python 2 42 32 164 HTML 1 0 0 94 Ruby 1 6 0 60 Go 1 10 15 20 ------------------------------------------------------------------------------- SUM: 896 24069 32193 132856 -------------------------------------------------------------------------------
実行結果を見ると、SwiftやGoなどいろんな言語にも対応しているので、clocだけで、いろんなプロジェクトに適用できそうです。
また、いろいろなオプションも用意されており、 例えば以下のようにオプションを指定することにより除外するディレクトリを指定したりできます。
$ cloc Hoge --exclude-dir=vendor
詳しくはドキュメントを御覧ください
WebP.frameworkの作成方法
libwebpをclone
> git clone https://chromium.googlesource.com/webm/libwebp
最新のをcheckout
> git checkout 0.4.4
iosbuild.shを実行
> iosbuild.sh
iosbuild.shでautomakeがないとか怒られた場合は以下を入れる
Command line tools
> xcode-select --install
automake, autoconfとか
> brew install libtool > brew install automake > brew install autoconf
成功するとiosbuild.shを実行したディレクトリにWebP.frameworkが生成されます。
Swiftでコマンドラインでカバレッジを取る方法
Objective-Cのときはgcov使ってUnitTestのカバレッジを取っていましたが、 Xcode7から(?)llvm-covが使えるようになったのでこれを使ってみます。
ドキュメントを見ると使い方としては結構単純で、 レポートを出力するには、以下のようにPROFILEとBINを与えてやればレポートが出力されます。
llvm-cov report [options] -instr-profile PROFILE BIN [SOURCES]
PROFILEとBINは、コードカバレッジをONにしてテストを実行するとお馴染みのDerived Data以下に出力されます。
以上のことを踏まえてコマンドラインで実行すると以下のようにすることでカバレッジのレポートを出力させることができます。
注:{}内はプロジェクトによって変更してください。
xcodebuild test -scheme {SchemeName} -configuration Debug -destination 'platform=iOS Simulator,name=iPhone 6' -derivedDataPath build -enableCodeCoverage YES
xcrun llvm-cov report -instr-profile build/Build/Intermediates/CodeCoverage/{SchemeName}/Coverage.profdata build/Build/Intermediates/CodeCoverage/{SchemeName}/Products/Debug-iphonesimulator/{ProductName}.app/{ProductName}
結果はこんな感じで出力されます。
Filename Regions Miss Cover Functions Executed ----------------------------------------------------------------------- .../Headers/NSException.h 0 0 nan% 0 nan% ...eaders/NSObjCRuntime.h 0 0 nan% 0 nan% ...usr/include/MacTypes.h 0 0 nan% 0 nan% ...sdk/usr/include/math.h 0 0 nan% 0 nan% ...sr/include/objc/objc.h 0 0 nan% 0 nan% ...r/include/sys/_types.h 0 0 nan% 0 nan% ..._iOS/AppDelegate.swift 26 21 19.23% 14 21.43%
ただこれファイル名が長いと...省略されて見えなかったりするのがちょっと微妙。。
あと、CIと連動させて表示したいとかだとこのままだと厳しいですね
3D Touch Peak, Popの使い方
1. 3D Touchの発火元となるviewの登録
UIViewControllerのregisterForPreviewingWithDelegateというメソッドを使って、3D Touchに反応するViewと、3D Touchが発生した際にハンドリングするdelegateを登録します。
override func viewDidLoad() { super.viewDidLoad() imageView.userInteractionEnabled = true // imageViewの場合これが必要 registerForPreviewingWithDelegate(self, sourceView: imageView) }
2. UIViewControllerPreviewingDelegateを継承
3D Touchに反応するUIViewControllerにUIViewControllerPreviewingDelegateを継承させます
class ViewController: UIViewController, UIViewControllerPreviewingDelegate {
3. UIViewControllerPreviewingDelegateのメソッドの実装
UIViewControllerPreviewingDelegateの2つのrequiredのメソッドを実装します
previewingContext:viewControllerForLocation: の実装
こちらはpeak(プレビュー画面を表示)に対応します。
この中では、peakで表示するViewControllerをreturnします。
func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? { print("peak") let sb = UIStoryboard(name: "Storyboard2", bundle: nil) let vc = sb.instantiateInitialViewController() vc?.preferredContentSize = CGSize(width: 0, height: 200) // 表示サイズ print(previewingContext.sourceRect) return vc }
peakで表示するサイズはViewControllerのpreferredContentSizeで指定することができます。
previewwingContext.sourceRectは3D Touchした際にblurしない範囲(浮き出る部分)を示しています。 sourceViewがimageViewなどでやる場合は特に指定する必要はないですが、tableviewなどの場合、タッチされたcellの位置が浮き出る感じに表示されたほうがよいので、こういうときはpreviewwingContext.sourceRect = cell.frameという風に設定するのがよいと思われます。
previewingContext:commitViewController: の実装
こちらはpop(プレビュー画面で更に強く押して全画面表示)する際に呼ばれるメソッドです。 このメソッド内でpresentViewControllerやnavigationcontroller.pushViewControllerなどを使って次の画面に遷移させます。 引数のcommitViewControllerはpreviewingContext:viewControllerForLocation: でreturnしたViewControllerです。
このメソッドでなにもしないとpeak画面で更に強く押しても何も起こりません。(消える)
func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) { print("pop") presentViewController(viewControllerToCommit, animated: true) {} }
4. プレビューアクションの設定
プレビュー画面で下にスクロールしたら出てくるアクションの設定方法です。
プレビュー表示に使うViewController(previewingContext:viewControllerForLocation:でreturnするViewController)でpreviewActionItems() -> [UIPreviewActionItem]をオーバーライドします。
override func previewActionItems() -> [UIPreviewActionItem] { let action1 = UIPreviewAction(title: "action 1", style: .Default) { (previewAction, viewController) in print("action 1") } let action2 = UIPreviewAction(title: "action 1", style: .Destructive) { (previewAction, viewController) in print("action 2") } let subAction1 = UIPreviewAction(title: "subaction 1", style: .Default) { (previewAction, viewController) in print("sub action 1") } let subAction2 = UIPreviewAction(title: "subaction 2", style: .Default) { (previewAction, viewController) in print("sub action 2") } let groupedActions = UIPreviewActionGroup(title: "Sub Actions…", style: .Default, actions: [subAction1, subAction2] ) return [action1, action2, groupedActions] }
iOS9のクイックアクション対応
ホーム画面のアプリアイコンを3D Touchすると実行されるクイックアクションの実装方法です。
Info.plistの変更
UIApplicationShortcutItemsを追加します。
例
Info.plist
<key>UIApplicationShortcutItems</key> <array> <dict> <key>UIApplicationShortcutItemIconType</key> <string>UIApplicationShortcutIconTypeShare</string> <key>UIApplicationShortcutItemSubtitle</key> <string>shortcutSubtitle</string> <key>UIApplicationShortcutItemTitle</key> <string>shortcutTitle</string> <key>UIApplicationShortcutItemType</key> <string>$(PRODUCT_BUNDLE_IDENTIFIER).Second</string> <key>UIApplicationShortcutItemUserInfo</key> <dict> <key>secondShortcutKey1</key> <string>secondShortcutValue1</string> </dict> </dict> </array>
仕様
詳しい仕様はこちらのUIApplicationShortcutItemsを参照 https://developer.apple.com/library/prerelease/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/iPhoneOSKeys.html#//apple_ref/doc/uid/TP40009252-SW36
必須事項
必須なのは、クイックアクションの種類を表すStringであるUIApplicationShortcutItemType と 表示するクイックアクション名を表すUIApplicationShortcutItemTitle です。
アイコンの設定
システムの用意しているアイコンを利用する場合
UIApplicationShortcutItemIconTypeをkeyにして、UIApplicationShortcutItonTypeを指定します UIApplicationShortcutIcon Class Reference
- UIApplicationShortcutIconType
enum UIApplicationShortcutIconType : Int { case Compose case Play case Pause case Add case Location case Search case Share case Prohibit case Contact case Home case MarkLocation case Favorite case Love case Cloud case Invitation case Confirmation case Mail case Message case Date case Time case CapturePhoto case CaptureVideo case Task case TaskCompleted case Alarm case Bookmark case Shuffle case Audio case Update }
- 例
<key>UIApplicationShortcutItemIconType</key> <string>UIApplicationShortcutIconTypeShare</string>
独自アイコンを使う場合
UIApplicationShortcutItemIconFileをkeyにして、ファイル名を指定
- 画像の条件
- square, single color, and 35x35 point
AppDelegateの変更
クイックアクションが実行されるとAppDelegateのperformActionForShortcutItemが呼ばれます。 shortcutItemを参照すれば、どのクイックアクションが実行されたか判別できるので、それによって対応する処理を実装すればよいです。
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: Bool -> Void) { }
動的にクイックアクションを追加する方法
Info.plistに書く静的にクイックアクションを追加する方法とは別に、アプリ起動後にアプリのプログラムから動的にクイックアクションを追加することもできます
やることとしては、UIApplicationShortCutItemのインスタンスを生成して、UIApplicationのshortcutItemsにセットすればよいです。
if let shortcutItems = application.shortcutItems where shortcutItems.isEmpty { // Construct the items. let shortcut3 = UIMutableApplicationShortcutItem(type: "hoge", localizedTitle: "Play", localizedSubtitle: "Will Play an item", icon: UIApplicationShortcutIcon(type: .Play), userInfo: [ "": UIApplicationShortcutIconType.Play.rawValue ] ) application.shortcutItems = [shortcut3] }
Xcode7のCore AnimationのProfilerはiOS9には対応していない
訂正:Xcode7.1では治ってました
Xcode7を使って、InstrumentsのCore Animationを使って実機でパフォーマンスを測ろうとしても下の図のように実機を選択することができません
iOS8.4の端末を使って試してみたところ選択することができたので、iOS9にはまだ対応していないと思われます
SwiftでUnitTest時に環境変数で分岐させる
テスト対象を@testable import XXXXを使ってimportした場合に、テスト対象のコードをテスト時だけ分岐させて特定の処理を行わないようにしたりしたい場合の対処方法です。
方法としては、環境変数がセットされているかどうかでテストかどうかを判定します。
環境変数の追加
Edit Scheme→Test→Argumentsを選択すると以下の様な画面になります。
ここのEnvironment Variablesに任意の環境変数を定義します。
分岐処理の追加
上記で追加した環境変数は、NSProcessInfo().environmentを利用して取得することができます。
例えば環境変数が設定されているときはreturnするみたいな場合は以下のようにすればよいです。
guard NSProcessInfo().environment["環境変数名"] == nil else { return true } // テスト時は実行しないコード ...
dynamic frameworkを使ってるプロジェクトでコマンドラインでipaファイル作成
Swiftでサブプロジェクトのモジュールをdynamic frameworkで取り込む場合にコマンドラインでipaファイルを作成する手順です。
環境:Xcode7.1
従来のやりかたではできなかったのでメモしておきます
発生した問題
以前Objective-Cでstatic libraryでやっていたときは以下のようなやり方で作成できていました
$ xcodebuild -sdk iphoneos -target XXX -configuration Release clean build PROVISIONING_PROFILE=xxxxx-xxxx-xxxxxx-xxxxx $ xcrun -sdk iphoneos XXX build/Release-iphoneos/***.app -o build/***.ipa --embed ****.mobileprovision
が、xcodebuildを実行したところ以下のように、dynamic frameworkをビルドする際にcode sign errorが発生してしまいビルドがこけてしまいます。
$ xcodebuild -sdk iphoneos -target XXX -configuration Release clean build PROVISIONING_PROFILE=xxxxx-xxxx-xxxxxx-xxxxx Build settings from command line: PROVISIONING_PROFILE = xxxxx-xxxx-xxxxxx-xxxxx SDKROOT = iphoneos9.1 === CLEAN TARGET YyyYYYYY OF PROJECT YyyYYYYY WITH CONFIGURATION Release === Check dependencies [BEROR]Code Sign error: Provisioning profile does not match bundle identifier: The provisioning profile specified in your build settings (“xxxxxx”) has an AppID of “jp.xxxx.xxxxx” which does not match your bundle identifier “com.example.Yyyy”.
解決方法
xcocebuild build でprovisioning profileを指定するとdynamic frameworkもそのprovisioning profileを使ってビルドしようとしてcode sign errorが発生してしまうので、xcodebuild archiveをつかって一旦xcarchiveを生成して、ipaにexportする際にprovisioning profileを指定してやることによって解決しました。
$ xcodebuild -scheme XXXX archive -archivePath release/xxx.xcarchive $ xcodebuild -exportArchive -archivePath release/xxx.xcarchive -exportOptionsPlist yyy.plist -exportPath zzz.ipa
exportOptionsPlistは以下のような感じになります。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>teamID</key> <string>MYTEAMID123</string> <key>method</key> <string>app-store</string> <key>uploadSymbols</key> <false/> </dict> </plist>
methodの値を変更することで、app store向けやadhocなどビルド方法を切り替えられます。
* app-store
* ad-hoc
* package
* enterprise
* development
* developer-id
その他のexportOptionsPlist内で設定できる内容は↓で確認できます
$ xcodebuild -h
CHANGELOGの自動生成
リリースごとにCHANGELOGをもれなく書くのは結構な手間です。 そこでcommit時にコミットログとしてCHANGELOGを記載しておき、リリース時にそれをまとめて出力することでその手間を削減します。
conventional-changelogを使ってCHANGELOG.mdを生成する方法について記載します。
事前準備
Node.jsのインストール(詳細はここでは割愛)
conventional-changelogのインストール
$ sudo npm install -g conventional-changelog
プロジェクトの設定
package.jsonというファイルをプロジェクトのトップディレクトリに配置します。 そして、以下のように、package.jsonにname, version, respsitoryを記載します。
- package.json
{ "name": "SampleApp", "version": "1.2.0", "repository": { "type": "git", "url": "https://github.com/xxxx/xxxx" } }
CHANGELOG情報のコミット
commitする際にCHANGELOGにのせる場合は、指定のフォーマットでcommitログ記載します。
angular, atomなど、プロジェクトによって独自のフォーマットが使われていて好みのものを利用します。
詳細はこちらを参照してください。 https://github.com/ajoslin/conventional-changelog/tree/master/conventions
CHANGELOGの生成
package.jsonを置いたのディレクトリ上で以下のコマンドを実行します。 (eslintのところはフォーマットによって変わります)
$ conventional-changelog -p eslint -i CHANGELOG.md -w
そうするとCHANGELOG.mdが生成されます。
ワークフロー
その後は、以下のようなワークフローでCHANGELOGを生成します。
Swift2 ドキュメントコメント
ドキュメントコメントを記載することで、Option + クリックでメソッドなどの説明を表示したりするようにすることができます。
ドキュメントコメントの指定方法は2通りあり、複数行の場合は/* ... /で囲い、一行の場合は///で始めます。
/** say message */ func say(message: String) -> String {
/// say message func say(message: String) -> String {
関数の説明
テキストをそのままかけば関数の説明として扱われます
/// say message func say(message: String) -> String {
引数の説明
引数一個の場合
/// say message /// - parameter message: a message what you want to say func say(message: String) -> String {
引数複数の場合
/// say message /// - parameters: /// - message: a message what you want to say /// - language: Japanese or English func say(message: String, language: String) -> String {
戻り値の説明
/// say message /// - returns: a message what did say func say(message: String) -> String {
エラーの説明
/// say message /// - throws: ServerError func say(message: String) throws -> String {
その他の記法
Markdown記法を使うことができます
/// # Title /// /// ## List /// - a /// - b /// - c /// /// ## Number List /// 1. list1 /// 2. list1 /// 3. list1 /// /// --- /// /// ## Reference /// [Link](http://github.com)
UnitTestでNSUserDefaultsに保存したデータを消す方法
UnitTestだとremoveObjectForKeyや
NSUserDefaults.standardUserDefaults().removeObjectForKey("key")
resetStandartUserDefaultsを呼んでもデータが消えません
NSUserDefaults.resetStandardUserDefaults()
解決方法
setObjectでnilを突っ込む
NSUserDefaults.standardUserDefaults().setObject(nil, forKey: "key")
Xcode7でシミュレーターのOSバージョンが表示されない不具合解消方法
Xcode7を使っていたところ、下の図のようにシミュレーター一覧を見ると、OSのバージョンが表示されず、どのOSなのかさっぱりわからなくなりました。。
解決方法
$ rm -rf ~/Library/Developer/CoreSimulator/Devices
そしてmacを再起動します