Monthly Archives: 1月 2015

受託開発などでプロビジョニングプロファイルを受領してアーカイブしたファイルをxcodebuildコマンドでエクスポートする方法

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので十分ご注意ください。

※開発準備、申請まわりの情報をお探しの方はiOSアプリの開発準備と申請の手順まとめを参照ください。

自分名義ではないプロビジョニングプロファイルにてビルド/アーカイブしたファイルをエクスポートする方法。以前は簡単でしたが、最近のXcodeだと手間がかかるようになりました。以前から簡単に出来る方法を模索していたのですが、結局のところxcodebuildで実行する方法が確実のようで、備忘録も兼ねて書き留めておきます。

■前提条件

・先方名義の証明書とそれに紐づくプロビジョニングプロファイルを(先方名義ではない)自社Macにインポートした状態
・先方の開発者アカウントを知らない(UID/PWDを開示いただいていない)
 →UID/PWDを開示いただいている場合はXcodeにアカウントを登録すればOKですので、このエントリーに書いてある手順は不要です

■手順

(1) 通常通りアーカイブを実行します

(2) アーカイブ終了後、Organizerにて当該アーカイブ・ファイルのパスを取得します

メニューからShow in FinderでFinderを開きます。

ここからパスを取得する方法はいくつかありますが、

このあとTerminalで作業しますのでTerminalにドラッグしてパスを表示し、そのパスをどこかにコピペして保持します。

(3) コマンドラインからエクスポートします

$ xcodebuild -exportArchive -archivePath [アーカイブファイルパス] -exportProvisioningProfile [プロビジョニングプロファイル名] -exportPath [出力ipaファイルパス]

コマンド、オプションの基本形は上記のとおりです。

ここでは、

・アーカイブファイルパス
 /Users/***/Library/Developer/Xcode/Archives/2015-01-22/testapp\ 2015-01-22\ 21.53.xcarchive
・出力ipaファイルパス
 testapp.ipa
・プロビジョニングプロファイル名
 testapp
 →(注意)testapp.mobileprovisionなら拡張子を外したtestapp(Xcode側の設定を見たほうが確実です!)

として実行例を載せます。

 
$ xcodebuild -exportArchive -archivePath  /Users/***/Library/Developer/Xcode/Archives/2015-01-23/testapp\ 2015-01-23\ 15.43.xcarchive -exportProvisioningProfile testapp -exportPath testapp.ipa
2015-01-23 15:47:32.658 xcodebuild[8853:70352] [MT] PluginLoading: Required plug-in compatibility UUID ******* for plug-in at path '~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/InjectionPlugin.xcplugin' not present in DVTPlugInCompatibilityUUIDs

..snip..

  adding: Payload/testapp.app/testapp 	(in=1482464) (out=519625) (deflated 65%)
total bytes=9321690, compressed=7880629 -> 15% savings
]
Results at '/var/folders/xw/ys67g61173n0nbdqqv7ly4ch0000gn/T/*******/testapp.ipa' 
Moving exported product to 'testapp.ipa'
** EXPORT SUCCEEDED **

これで指定した出力場所にipaがエクスポートされます。


pod installでAnalyzing dependenciesのまま止まってしまう

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので十分ご注意ください。

pod installがAnalyzing dependenciesの状態で止まってしまい、プロンプトが返ってこなくなる時があります。

$ pod install
Analyzing dependencies

これを解決します。

一旦、リポジトリを削除し、

$ pod repo remove master

# 必要であればcocoapods/のバックアップを取ってから実行してください

再度セットアップし、

$ pod setup

再度インストールを試みます。

$ pod install
Analyzing dependencies
Downloading dependencies
Installing ********
Using ********
Generating Pods project
Integrating client project

ほとんどの場合これでうまくいくはずです。


Could not launch “*********************” process launch failed: failed to get the task for process ***** の原因

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので十分ご注意ください。

実機を接続してRun後、アプリが起動するかしないかのタイミング、起動直後に画面遷移させたり触ったりすると、突然ポップアップを出してアプリが落ちることがあります。

Could not launch “*********************” process launch failed: failed to get the task for process 22530 

ProvisioningProfileにAdHoc用のものを設定している状態で実機デバッグを行っている場合によく出ます。解決方法は簡単、通常の開発用のProvisioningProfileを指定すればOKです。Schemeで切り替えている場合は、Schemeに設定しているConfigurationが正しいか、あるいは、選択しているSchemeが間違っていないか確認しましょう。


画面遷移の際に whose view is not in the window hierarchy! となる場合の対処

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので十分ご注意ください。

ある画面から別の画面に遷移する際、下記のような警告が表示される場合があります。

2015-01-16 13:41:45.579 ********[17479:7800042] Warning: Attempt to present <UINavigationController: 0x14e540f50> on <UINavigationController: 0x14e60a4f0> whose view is not in the window hierarchy!

これは、表示元の親画面(ViewController)がViewの階層スタックに登録される前に、遷移先の画面を表示しようとした場合が主な原因です。viewDidLoadで画面を遷移させようとしていないでしょうか。 遷移ロジックをviewDidAppearに移してみましょう。

オブザーバーなど通知で飛んできた場合など遷移ロジックによっては画面生成まわり(viewDidLoadやviewDidAppearなど)に遷移ロジックを書かない場合もありますので、上記の方法では対処できません。このような場合は、

dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5f * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
	// 遷移ロジック
});

このように遷移処理をディレイ(ここでは値0.5にしています)させ、画面生成の間を与えることで対処できるケースがあります。


UITableViewを組み込んだViewを表示する度にテーブル全体を更新(reloadData)したい場合

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので十分ご注意ください。

画面を表示(≠メモリロード)する度にテーブル全体を更新をしたい場合は、以下のようにviewWillAppear内にreloadDataを記述します。

- (void)viewWillAppear:(BOOL)animated
{
        [self.tableView reloadData];
        [super viewWillAppear:animated];
}

重要なのはsuperクラスのメソッドを呼ぶ前でリロードすることです。先に呼んでしまうと2回ロードされるなど、うまく動作しません。やってしまいがちなのは、viewDidLoadやviewDidAppear内にreloadDataを記述し、フラグなどで制御する方法です。それでも可能ですし、そのような方法しか取れないプログラム仕様もあるかとおもいますが、上述の方法が一番スッキリします。

なお、テーブル全体ではなく部分的に更新したい場合もあるかとおもいますので、以下簡単に載せておきます。

// 指定した1つのセクションのみ更新
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationNone];
// 複数のセクションを指定して更新
NSRange range = NSMakeRange(0, 3);
[self.tableView reloadSections:[NSIndexSet indexSetWithIndexesInRange:range] withRowAnimation:UITableViewRowAnimationNone];
// 指定したセルのみ更新
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:1 inSection:0];
[[self.tableView cellForRowAtIndexPath:indexPath] setNeedsLayout];
// 画面上に見えている範囲のセルのみ更新
for (UITableViewCell *cell in [self.tableView visibleCells]){
    [self updateCell:cell atIndexPath:[self.tableView indexPathForCell:cell]];
}

余談ですが、セルを一括して追加削除する場合、

[self.tableView beginUpdates];
     // ここに追加や削除の処理を記述
[self.tableView endUpdates];

このようにbeginUpdates/endUpdatesで囲むと、indexPathを明示的に処理しなくても自動的に調整してくれます(endUpdatesが呼ばれた後にcellForRowAtIndexPathが呼ばれる)。