CIのサービスいろいろ
build.gradleからAndroidのバージョンベタ書きを避ける
Androidでgradleを使うと、デフォルトではこんな感じにバージョン情報がベタ書きで生成されます。
apply plugin: 'android' android { compileSdkVersion 17 buildToolsVersion "21.1.2" defaultConfig { minSdkVersion 8 targetSdkVersion 17 } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } }
build.gradle一つですむプロジェクトであれば、これでもよいかもですが、マルチプロジェクトにしている場合だと変更する場合にたくさん書き換えることとなりミスの元となります。
そこで以下のようにgradle.propertiesに切り分けておきます。
gradle.properties
ANDROID_BUILD_MIN_SDK_VERSION=9 ANDROID_BUILD_TARGET_SDK_VERSION=21 ANDROID_BUILD_TOOLS_VERSION=21.1.2 ANDROID_BUILD_SDK_VERSION=21
build.gradle
android { compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION defaultConfig { minSdkVersion Integer.parseInt(project.ANDROID_BUILD_MIN_SDK_VERSION) targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) }
参考
https://github.com/facebook/facebook-android-sdk/blob/master/gradle.properties
https://github.com/facebook/facebook-android-sdk/blob/master/facebook/build.gradle
AndroidStudioでプロジェクトを開こうとするとCause: org/codehaus/groovy/runtime/typehandling/ShortTypeHandling
AndroidStudioでbuild.gradleを指定してプロジェクトを開こうとすると
Cause: org/codehaus/groovy/runtime/typehandling/ShortTypeHandling
というダイアログが表示されてプロジェクトが開かない・・・ という状態になりました。
gradle-wrapper.propertiesのgradleのバージョンが古い(1.x.xとか)とこうなるようで
gradle/wrapper/gradle-wrapper.propertiesを以下のように2.2.1にすると解決
distributionUrl=http\://services.gradle.org/distributions/gradle-2.2.1-all.zip
Android NDKでerror: 'to_string' is not a member of 'std'
androidでC++のstd::to_stringを使うと以下のようなビルドエラーがでました。(環境:Android NDK r10d)
error: 'to_string' is not a member of 'std'
もちろんC++11の設定自体はできていて、他のstd::functionなどは問題なく使えます。
LOCAL_CFLAGS := -std=c++11
NDKのlibstdc++ではまだto_stringをサポートしてない?ぽいので自前で用意するしかなさそうな
template <typename T> std::string to_string(T value) { std::ostringstream os ; os << value ; return os.str() ; }
APP_STL を gnustl_staticではなく、stlport_staticにするとto_string使うことができましたが、C++ exception supportがない模様
The Developer's Guide | Android Developers
参考
Swiftで引数リスト(CVaListPointer)の渡し方
SwiftからObjective-Cのメソッドを呼ぶ際に、引数が引数リスト(va_list)の場合、Swiftでは型が以下のようなCVaListPointerになります。
class func raise(_ name: String, format format: String, arguments argList: CVaListPointer)
結論からいきますと、getVaListという関数を使って、getVaListの引数に引数リストとして渡したいArrayを突っ込めばよいです。
NSException.raise("hoge exception", format:"exception raised %d", arguments:getVaList([1]))
以下脱線
CVaListPointerの定義を調べてみるとこのようになるほどわからん感じの構造体です。
/// The corresponding Swift type to `va_list` in imported C APIs. struct CVaListPointer { init(_fromUnsafeMutablePointer from: UnsafeMutablePointer<Void>) }
CVaListPointerを生成するためのメソッドが用意されていてこれを使う。
/// Returns a `CVaListPointer` built from `args` that's backed by /// autoreleased storage. /// /// .. Warning:: This function is best avoided in favor of /// `withVaList`, but occasionally (i.e. in a `class` initializer) you /// may find that the language rules don't allow you to use /// `withVaList` as intended. func getVaList(args: [CVarArgType]) -> CVaListPointer
CVarArgTypeはprotocol
protocol CVarArgType { /// Transform `self` into a series of machine words that can be /// appropriately interpreted by C varargs func encode() -> [Word] }
ということからgetVaListの引数には、CVarArgTypeプロトコルを実装しているクラスのArrayということになります。
Intなどの型はCVarArgTypeをextensionで実装してるので使えます。
extension Int : CVarArgType { /// Transform `self` into a series of machine words that can be /// appropriately interpreted by C varargs func encode() -> [Word] }
調べた感じだとStringは一見CVarArgTypeを実装していないように見えるんですが、ArrayにString入れても使えるのはなんでかな・・
xcodebuildでdevice向けのビルドするときのdestinationの指定方法
以前xcodebuildのパラメータの指定方法についてこちらに書きました。
Travis CIでObjective-C/Swiftのテストを実行する - Pragmatic ball boy
テストするときはシミュレータを指定するのでこれで問題ないのですが、 実機向けのstaticライブラリを用意したりするときには、destinationの指定方法が以下のように変わります。
- -destination(必須)
ここで問題なのが、nameとidのところなんですが、ビルドマシンに実機を常に指しておけば指定できますが、たいていはそういう使い方しないと思います。
XcodeからビルドするときはiOS Deviceを指定すれば実機なくてもビルドだけはできるのでそれと同じことはxcodebuildでもできるはず。
xcodebuildのドキュメントを見るとgeneric/をつければ良いと書いてあります。
To build against a platform generically instead of a specific device, the destination specifier may be prefixed with the optional string "generic/", indicating that the platform should be targeted generically.
よって、以下のようにgeneric/platform=iOSと指定することでiOS Deviceを指定するのと同じようにビルドすることができます。
$ xcodebuild -scheme MyScheme -destination 'generic/platform=iOS' build
ちなみに実機とsimulator両方で使えるfat binaryを作りたい場合は、destinationは複数指定できるので以下のように、2つdestinationを使います。
$ xcodebuild -scheme MyScheme -destination 'generic/platform=iOS' -destination 'platform=iOS Simulator,name=iPhone 6,0S=8.0' build
fat binaryに含まれているアーキテクチャを確認するには以下のコマンドを使えばよいです。
$ xcrun lipo -info *.a
FragmentでUnfortunately, XXX has stoppedでクラッシュする
Android StudioのNew->Fragmentで追加したFragmentを使って表示しようとすると
"Unfortunately, XXX has stopped"
というポップアップが出てクラッシュするという問題が発生。 Logにも何も出ていない・・・
どこでクラッシュしてるのかさっぱりわからなかったので、仕方なく追加したFragmentにブレークポイントをはりまくって確認したところ、onAttachを最後にお亡くなりになっていた。
@Override public void onAttach(Activity activity) { super.onAttach(activity); try { mListener = (OnFragmentInteractionListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnFragmentInteractionListener"); } }
Fragmentを呼ぶActivity側をOnFragmentInteractionListener interfaceに対応させないとダメのようだ。
よって以下のようにActivityにimplements XXXFragment.OnFragmentInteractionListener を追加し、onFragmentInteractionをオーバーライドすればOK。
public class XXXActivity extends ActionBarActivity implements XXXFragment.OnFragmentInteractionListener { /// 省略 @Override public void onFragmentInteraction(Uri uri) { }
よく見たら作成されたFragmentに"Activities that contain this fragment must implement the XXXFragment.OnFragmentInteractionListener" て書いてありましたね・・、よく読めってことか
/** * A simple {@link Fragment} subclass. * Activities that contain this fragment must implement the * {@link XXXFragment.OnFragmentInteractionListener} interface * to handle interaction events. * Use the {@link XXXFragment#newInstance} factory method to * create an instance of this fragment. */ public class XXXFragment extends Fragment {
追記:
Fragmentを追加する際に、optionで"Include interface callbacks?"にデフォルトでチェックが入っているため、今回のようになったようでした。
iOS保守するときに年に一回くらい使うwikipediaのリンク
32bit/64bit混成バイナリをiOS5.1.1端末にインストールできない
CocoaTouch 64ビット移行ガイド より、iOS5.1.1以降で32bit/64bitの混成バイナリが利用可能と書いてあります。
しかし、ipaファイルを作ってiOS5.1.1にインストールしようとすると失敗してしまいます。
AppleのDeveloper forumに情報があったのすが、 https://devforums.apple.com/message/1071370#1071370
iOS5.1.1では64bitの入った混成バイナリのインストールが不可能で、古いデバイスに対応するためにどうやらApple側でAppStoreにサブミットされたバイナリをiOS5.1.1用に64bitバイナリを含めないように加工してるのでは、、みたいな憶測が書かれていました。
実際AppStoreからダウンロードしたらインストールできたみたいなことは書いてあったので混成バイナリであってもiOS5.1.1で動作はするっぽいですが、事前に確認できないのはちょっと気持ち悪いですね。。
なので、どうしてもiOS5.1.1で動作確認したい場合は、iOS5.1.1の評価用にだけ32bitだけでビルドするように変更しなければならないということになります。
Genymotionのインストール
手元にAndroidデバイスがないけどちょっとエミュレーターで動かしたい場合に、通常のエミュレーターだと遅すぎて使い物にならないのでGenymotionを利用しています。
インストールした時の手順のメモです。
Genymotionのダウンロードとインストール
ここからダウンロード(要Login)
ダウンロードしたdmgファイルを開き、GenymotionとGenymotion ShellをApplicationフォルダにドラックアンドドロップ
VertualBoxのダウンロードとインストール
ここからダウンロード
Downloads – Oracle VM VirtualBox
ダウンロードしたdmgファイルを開き、VirtualBox.pkgをダブルクリックするとインストーラーが立ち上がるのでポチポチ押していけばインストールが完了します。
Genymotionの起動
ApplicationフォルダのGenymotionを開きます。
"You do not have any virtual device yet. Do you want to add a new one?" とダイアログが出るのでYesにします。
virtual deviceが何もない状態なので、下のピンクの枠をのSign inクリックしてログインします。
ログインすると下のようにvirtual deviceが取得され利用できるようになります。
あとは使いたいdeviceを選んで、nextを押していくとOK
そうするとvirtual deviceが作成されます
上のスタートボタンを押すと起動します
一瞬で起動するのでデフォルトのエミュレータとの差は雲泥の差ですね
参考
ユーザーガイド Genymotion
SwiftでUnitTestするときに"Use of undeclared type"になる場合の対処法
テストを実行するとこういうエラーがでることがあると思います。
Use of undeclared type 'Todo'
does not have a member named 'todo'
import XCTest import TodoApp class TodoTests: XCTestCase { var todo: Todo! override func setUp() { super.setUp() todo = Todo() }
たまに解決方法として全部publicにするとかいうのを見かけるのですが、それは間違いです。もしそうだとするとpublicなクラスしかテストできないってことになるので普通に考えたら明らかにおかしいですよね。。
ここから解決方法です。
方法その1
Swift1.x系での対処方法です。Swift2.x系でも使えます。
問題は、Test Targetの設定にあります。 ProjectのUnitTest用のTargetを選択し、Build Phasesのタブを選択します。
エラーがでる場合は、Compile Sourcesにテスト対象のクラスが追加されていないと思われます。
そこでこのようにテスト対象のクラスを追加してやればエラーは消えるはずです。
クラスをプロジェクトに追加される際に、どのTargetに対して追加するか聞かれるダイアログがでると思いますが、その際にテストTargetも対象にいれるのを忘れないように気をつけるとこういったことは防げます。
方法その2
Swift2.xから使える方法です。
importの前に@testableをつければよいです。
import XCTest @testable import TodoApp
RealmなどTestTargetでテスト対象のクラス一緒にコンパイルできない場合はこの方法でやるしかないです。
Travis CIでObjective-C/Swiftのテストを実行する
[環境]Xcode 6.1
Swift,Objective-Cどちらの場合でもこの方法でいけるはずです。
Travis CIにドキュメントが用意されていますが、xctoolを使った例が示されているので無視したほうがよいです。xctoolはAppleが用意した標準のビルドツールではなく、Facebookがその置換えとして作ったツールなので、Xcodeがバージョンアップするたびに大抵ぶっ壊れるので使わないほうが無難です。Travis CIでは何を間違ったのかデフォルトでxctoolを使っています、自分らでXcodeの変化についていけるようにメンテしてるならいいんですが、そこは不明。。ここではxcodebuildを使った方法を記載します。
Building an Objective-C Project - Travis CI
xcodebuild
command lineでテストを実行するにはxcodebuildを使います。
xcodebuild(1) Mac OS X Developer Tools Manual Page
xcodebuildには最低限以下のようなオプションを設定する必要があります
- -workspace
- -scheme(必須)
- -configuration
- -destination(必須)
そして、buildactionとして test を指定すれば、ビルド後テストが実行されます。
例
xcodebuild -workspace MyWorkspace.xcworkspace -scheme MyScheme -configuration Debug -destination 'platform=iOS Simulator,name=iPhone 6,0S=8.0' -destination 'platform=iOS Simulator,name=iPad Air,0S=7.1' test
destinationは複数設定できるので、複数のシミュレータで実行したい場合などは複数指定すれば、何度もxcodebuild testを実行する必要はありません。
引っかかりやすいポイント
scheme
schemeがgit repositoryで管理されていない状態でxcodebuildを実行すると
xcodebuild: error: The project 'XXXX' does not contain a scheme named 'XXXX'.
とエラーが出てしまいます。 schemeの情報は通常だとxcuserdataいかに保存されるようですが、ユーザー毎の情報なので普通はrepositoryからは.gitigoreで排除しているはず。それではどうするかというとxcshareddataとしてrepositoryにいれます。 方法は、以下のように"Manage schemes..."を選択し、scheme一覧を表示し、"shared"のチェックボックスにチェックをいれてcloseするとxcshareddataが生成されているのでrepositoryにコミットすればよいはず。
destinationの name と OSの対応の調べ方
以下のコマンドを実行します
> xcrun simctl list
そうすると以下のようにOSのバージョンとそれに対応するシミュレーターのリストが出てくるのでこれを使えばよいです。
== Device Types == iPhone 4s (com.apple.CoreSimulator.SimDeviceType.iPhone-4s) iPhone 5 (com.apple.CoreSimulator.SimDeviceType.iPhone-5) iPhone 5s (com.apple.CoreSimulator.SimDeviceType.iPhone-5s) iPhone 6 Plus (com.apple.CoreSimulator.SimDeviceType.iPhone-6-Plus) iPhone 6 (com.apple.CoreSimulator.SimDeviceType.iPhone-6) iPad 2 (com.apple.CoreSimulator.SimDeviceType.iPad-2) iPad Retina (com.apple.CoreSimulator.SimDeviceType.iPad-Retina) iPad Air (com.apple.CoreSimulator.SimDeviceType.iPad-Air) Resizable iPhone (com.apple.CoreSimulator.SimDeviceType.Resizable-iPhone) Resizable iPad (com.apple.CoreSimulator.SimDeviceType.Resizable-iPad) == Runtimes == iOS 7.0 (7.0.3 - 11B507) (com.apple.CoreSimulator.SimRuntime.iOS-7-0) iOS 7.1 (7.1 - 11D167) (com.apple.CoreSimulator.SimRuntime.iOS-7-1) iOS 8.1 (8.1 - 12B411) (com.apple.CoreSimulator.SimRuntime.iOS-8-1) == Devices == -- iOS 7.0 -- iPhone 4s (0FF14361-F567-4B40-9FE4-79D572867676) (Shutdown) iPhone 5 (29226DD1-620D-4746-911E-CEC9A5B76A94) (Shutdown) iPhone 5s (CD01CF0C-5715-499D-A1F2-96E1E7D043CF) (Shutdown) iPad 2 (C884ACD1-D4D7-4391-9147-882B2C22B394) (Shutdown) iPad Retina (8C7D647C-7024-4E44-8555-4273A4264891) (Shutdown) iPad Air (33CDAD90-98F8-49FE-8EF9-51CE0F7B9043) (Shutdown) -- iOS 7.1 -- iPhone 4s (2FA61CBE-0077-44C2-A981-2B9B09A53C77) (Shutdown) iPhone 5 (9FE60E90-DB4A-4975-885A-087171F27099) (Shutdown) iPhone 5s (42A87F6B-BB69-499A-BB3A-FBBF4F8BDBCF) (Booted) iPad 2 (1A35A3AE-6F13-499F-BF8D-3AF0CF23096E) (Shutdown) iPad Retina (953DF503-1B98-4A74-8784-42680F1498C1) (Shutdown) iPad Air (9B94DE85-F421-4C2B-B570-D78D31034F16) (Shutdown) -- iOS 8.1 -- iPhone 4s (10AB167F-7C6F-4773-A9F0-4E3F66E2A4E4) (Shutdown) iPhone 5 (A12F3ACD-1DCE-4666-A1FA-443AA6AECC39) (Shutdown) iPhone 5s (7597741D-4654-4AC3-83A0-C82C81AFFFF7) (Shutdown) iPhone 6 Plus (EFE25990-D630-4D91-9A16-29479BAC6C44) (Shutdown) iPhone 6 (6A000F08-7D65-4894-B4AA-8A9A20CD57E3) (Shutdown) iPad 2 (CF8E7FC1-CC33-4F21-AA17-6DA5812DEA51) (Shutdown) iPad Retina (7AA79E2F-C397-46AC-B0F4-A291EB7A3B62) (Shutdown) iPad Air (09476634-4289-437E-B2EB-E4BDB4BFD6DA) (Shutdown) Resizable iPhone (131ED5C5-7E49-46CE-A72F-9FB2283B5D70) (Shutdown) Resizable iPad (24C58956-E9EF-4295-80BC-676343C8FA1B) (Shutdown)
command lineでのテスト実行方法がわかったので、後はTravis CIでこれを実行できるようにします。
.travis.yaml
Travis CIでテストを実行するgitリポジトリのrootに.travis.yamlファイルを作成します。 Swift、Objective-Cの場合以下を記載。
language: objective-c script: xcodebuild -workspace MyWorkspace.xcworkspace -scheme MyScheme -destination 'platform=iOS Simulator,name=iPhone 6,OS=8.1' test
script以下はなんでもよいのでxcodebuildを別のファイルに切り出して、そのファイルを実行するとかでもよいです。そのほうがtravis CI用途以外でも使えるので。
Appleのアプリの審査期間の目安
リリース時期が決まっているときなどはこれを見てどれくらい前に審査に出せば間に合いそうかチェックするのに役立ちます
Average App Store Review Times
審査期間はiOS8以降ものすごく長くなっていて最近はだいたい10~11日。in reviewに入るまでがやたらと長く、一旦in reviewになるとわりとさくっと終わる感じがします