「iPhone」カテゴリーアーカイブ

【Objective-C】BSXPCMessage received error for message: Connection interrupted というエラーメッセージ

【Xcode6.2 + iOS 8.4(iPhone5) + MacOX10.9.5】

Xcode5でiOS7をターゲットにして作ったアプリの改修をしていて、Xcode6.2でiPhone5の実機にビルドすると「BSXPCMessage received error for message: Connection interrupted」というメッセージが出てきます。

※同じ環境で、iPhone4S(iOS 7.1.2)では発生せず。

起動時のみログにメッセージが表示されるだけで、動作そのものには影響なさそうだけど、気になるので調べてみました。

【参考】ios – BSXPCMessage received error for message: Connection interrupted – Stack Overflow

このサイトを見てみると、どうやら「iOS 8でCIFilterのバグが原因」ということらしい。

CIContextのcontextWithOptionsで指定するoptionに、iOS8以降で項目(kCIContextPriorityRequestLow, kCIContextWorkingFormat)が追加されているので、明示的にオプションを指定しないとダメということなのかな?

【参考】+ contextWithOptions: – CIContext Class Reference
【参考】Context Options – CIContext Class Reference

とりあえずこのアプリはiOS7以上になっているので、「kCIContextUseSoftwareRenderer」を指定することでエラーは消えました。

【修正前】

_imageContext = [CIContext contextWithOptions:nil];

【修正後】

NSDictionary *contextOptions = [NSDictionary dictionaryWithObjectsAndKeys:
                                    [NSNumber numberWithBool:YES],kCIContextUseSoftwareRenderer,nil];
_imageContext = [CIContext contextWithOptions:contextOptions];

※ imageContextはCIContextとして定義されています。

@property (nonatomic, strong) CIContext *imageContext;

その他、下記のサイトを参考にしました。感謝。

超できるかな(Swiftでカメラアプリ1)
Study Swift: CIFilter – Invert the photo color by built-in filter
CoreImageで画像の加工をする その3「GPUにするべきかCPUにするべきか」 – Teratoma

手持ちの環境が対応してないので確認出来ないんだけど、Xcode6.3では直っているのかしらん?

【Xcode】Audio Uint関連リンク

あるアイデアが浮かんだので、Audio Uint関連を調べてみたけど、現状まだよくわかりません。。。

リンクばかり増えてSafariがタブだらけなので、まとめておきます。

・Objective-C – iOSで特定の周波数の音を鳴らす方法 – Qiita
これはやってみた。iOS8からは一部変更されているようで、warningが全ては消えないものの、とりあえずXcode6.2で動作しました。変更点はここ(CoreAudio Changes/iOS Developer Library — Prerelease)にあるけど、イマイチ理解できず。

よさそうなのが、今や入手困難な「iPhone Core Audioプログラミング」の著者が書いたブログ。書籍のベースになった記事らしい。この順に読むのがいいらしい。途中まで読んだ。

・Getting Started With Audio Unit
・Introduction to Audio Unit Development
・iPhone Core Audio

本家Appleのドキュメント

・Audio Unit Hosting Guide for iOS

あとコレ。第10〜15回までAudio関連の話。

・実践! iPhoneアプリ開発 (10) 楽器アプリの作り方 (1) – iPhoneのオーディオフレームワーク | マイナビニュース

Core Audio関係はこの辺り。後で必要になるかもしれないので。ただ一般的な話とか、オーディオファイル再生みたいな話は、前にMusicPlayerを試作したときにある程度調査済。

・iOS CoreAudioを使う上で参考になったサイト – 日々の記録。
・iOS Core Audio | シリーズ |Developers.IO
・Core Audioの概要(PDF)
・Core Audioの復習 | Second Flush

iOSには直接関係しないけど、Web Audio API関連。

・Web Audio API を使用してウェブブラウザをギターのチューナーにする件. | hirooka.pro
・Web Audio API の Oscillator で楽器を作りたい話 – Mach3.laBlog

これも直接関係ないけど、周波数の話として。
・ギターの音程とフレット/ELECTRIC GUITAR & BASS SUPER MANUAL

今のところ、見つかったのはこんなところで。やっぱり資料的にはObjective-Cの方が潤沢。特にあまり情報のないものは。

【2015.8.3 追加】
・Audio Unit 再入門 – Over&Out その後

アプリの宣伝についてのリンクなど

アプリの宣伝などについてのリンクあれこれ。とりあえずメモ。

AppBank Network – iOS.Androidアプリの収益化を実現する広告ネットワーク
【保存版】無料でできるアプリプロモーション一覧 | albatrus.com
Programmer’s Report: iPhone アプリ宣伝方法を考える
アプリリリースを無料で告知できちゃう!iPhonePLUSさんの新サービス使ったらランキングがワッショイ!ワッショイ! – あぷまがどっとねっと
ASCII.jp:今すぐできるASOの基本と7つの改善事例 (1/4)|失敗しないスマホアプリ企画&マーケティング

【Objective-C】iOSで音声認識サンプル(UIDictationController)

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

音声認識機能を調べていて、OpenEarsは日本語には今ひとつなので、非公式クラスらしけど、UIDictationControllerを使ってテストしてみました。

【参考】
iOS 5.1 の音声入力を使ってアプリケーションを操作してみる – 24/7 twenty-four seven
【iOS】【JPlayer】再生/停止の切り替えで音声認識をスタートして自動でストップする – The jonki

何故か1つ目のリンクのサンプルはうまく動かなくて、2つ目のリンクの方は音楽プレーヤーに組み込まれているので、音声認識だけを切り出してサンプルを作ってみました。

参考にしたリンクによれば、音声認識はキーボード経由でのみ行われ・・・

1.キーボードを表示。
2.音声入力ボタンを押す。
3.話す。
4.完了ボタンを押す。

という手順を踏んだ後、録音された音声がサーバ経由で文字列変換されて戻ってくるらしいです。

で、上記2.と4.を自動化するメソッドが、UIDictationControllerという非公開クラスに含まれています。

※キーボードは、UITextInputを紐付けた不可視のUIViewを用意して、becomeFirstResponderで表示する。
※UITextInputを使うので、必要なメソッドが定義されていないと、かなりの数のエラーがでます。警告だけど。

このサンプルでは、開始ボタンを押すと音声認識が開始(startDictation)され、timer(4.75秒)もしくは停止ボタンで音声認識が終了(stopDictation)するようになっています。画面はこんな感じ。

IMG_7121

認識率はさすがにいいです。コレ、早く公開APIにしてくれたらいろいろ使えそうなのになあ。iOS5.1からずっと非公開ってことは何かあるんですかね、問題が。

音声認識を使ったアプリのアイデアはあるんですが、OpenEarsで試してみるか、この方式で作っておいて公開APIになるまで寝かしておくか、悩みどころです。

一応、ソースをGitHubに上げてみました。初めてのGitHub(笑)。ご参考までに。

【Objective-C】アニメーションの終了イベント

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

とりあえずこんな感じで。delegateを設定して、終了時に呼ばれる関数をsetAnimationDidStopSelector:@selectorで指定する。

//アニメーション:設定
[UIView setAnimationsEnabled:YES];
[UIView beginAnimations:@"Flip" context:nil];
[UIView setAnimationDelegate : self ];
[UIView setAnimationDuration:0.5f];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:card cache:YES];
[UIView setAnimationDidStopSelector:@selector(compareCard)];

//アニメーション:開始
[UIView commitAnimations];

※card:アニメーションするUIView、compareCard:アニメーション終了時に呼ばれる関数

【参考】
あじもんすたー日記,iOSアニメーションでのミス

【Objective-C】再生リストからiCloudとDRM付きの曲を外す

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

前回書いたように、AVPlayer(AVQeuePlayer)でiPodライブラリの音楽を再生する場合、DRM付きの曲が再生できない。表示されているのに再生できないのはアプリとしてマズいので、再生リストから外す方法を考えてみました。

#import "SelectViewController.h"

@interface SelectViewController ()

@end

const int CATEGORY_ALBUM = 0;
const int CATEGORY_PLAYLIST = 1;
const int CATEGORY_ARTIST = 2;

@implementation SelectViewController
{
    NSDictionary *dataSource;
    NSMutableArray *key;

    int current_category;
}

//(中略)

-(void)setMusicCategory
{
    MPMediaQuery *query;
    switch (current_category) {
        case CATEGORY_ALBUM:
            query = [MPMediaQuery albumsQuery];
            break;
        case CATEGORY_PLAYLIST:
            query = [MPMediaQuery playlistsQuery];
            break;
        case CATEGORY_ARTIST:
            query = [MPMediaQuery artistsQuery];
            break;

        default:
            break;
    }

    //iCloudデータをフィルタリングで除外
    [query addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:[NSNumber numberWithBool:NO] forProperty:MPMediaItemPropertyIsCloudItem]];

    NSArray *collectionList = query.collections;
    key = [[NSMutableArray alloc] init];
    NSMutableArray *albumList = [[NSMutableArray alloc] init];

    for (MPMediaItemCollection *collection in collectionList) {
        MPMediaItem *representativeItem = [collection representativeItem];
        NSString *albumtTitle = [representativeItem valueForProperty:MPMediaItemPropertyAlbumTitle];

        //DRMチェック(AVPlayerで再生できないため/DRM対象曲のみを弾く)
        int drm_count = 0;

        NSMutableArray *itemList = [[NSMutableArray alloc] init];

        for (MPMediaItem *item in collection.items) {
            NSURL *url_item = [item valueForProperty:MPMediaItemPropertyAssetURL];
            if (url_item == NULL) {
                drm_count++;
            } else {
                [itemList addObject:item];
            }
        }

        if (itemList.count > 0) {
            MPMediaItemCollection *collection_new = [MPMediaItemCollection collectionWithItems:itemList];

            [albumList addObject:collection_new];

            NSString *keyword;
            switch (current_category) {
                case CATEGORY_ALBUM:
                    keyword = albumtTitle;
                    break;
                case CATEGORY_PLAYLIST:
                    keyword = [collection valueForProperty:MPMediaPlaylistPropertyName];
                    break;
                case CATEGORY_ARTIST:
                    keyword = [representativeItem valueForProperty:MPMediaItemPropertyArtist];
                    break;

                default:
                    break;
            }

            [key addObject:[NSString stringWithFormat:@"%@",keyword]];
        }
	}

    dataSource = [NSDictionary dictionaryWithObjects:albumList forKeys:key];
}

※ SelectViewControllerは、UITableViewControllerを継承したカスタムクラスです。

上に書いた関数「setMusicCategory」は、UITableViewに選択させる再生リストを表示するためのdataSourceを作成するための関数で、ボタンなどによって再生リストの種類(プレイリスト/アルバム/アーティスト)を切り換えたときに・・・

current_category = CATEGORY_ALBUM;
[self setMusicCategory];
[self.myTableView reloadData];

※self.myTableViewは、UITableViewControllerに含まれるUITableView。

みたいな感じで使います。

・iCloudのデータを除外する
これはフィルタ使って弾けるので、上のソースのこの部分。

//iCloudデータをフィルタリングで除外
[query addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:[NSNumber numberWithBool:NO] forProperty:MPMediaItemPropertyIsCloudItem]];

・DRM付きの曲を除外する
1つのアルバムで、DRMつきとDRMなしが混在することはないと思われるけど、プレイリストは任意に作れるので、引っ張ってきたMPMediaItemCollectionから、MPMediaItemのMPMediaItemPropertyAssetURLをひとつずつチェックしていくしかなさそうです。

for (MPMediaItem *item in collection.items) {
    NSURL *url_item = [item valueForProperty:MPMediaItemPropertyAssetURL];
    if (url_item == NULL) {
	drm_count++;
    } else {
	[itemList addObject:item];
    }
}

一応、コレで問題なさそうです。もっと効率的な方法があったら教えてください。

【Objective-C】AVQeuePlayerでMPMediaItemPropertyAssetURLがnullになる

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

AVPlayer(AVQeuePlayer)でiPodライブラリの音楽を再生するは、MPMediaItemからMPMediaItemPropertyAssetURLを取りだして、AVPlayerItemを生成する必要がある。

MPMediaItemPropertyAssetURL
A URL pointing to the media item, from which an AVAsset object (or other URL-based AV Foundation object) can be created, with any options as desired. Value is an NSURL object.

The URL has the custom scheme of ipod-library. For example, a URL might look like this:

ipod-library://item/item.m4a?id=12345
Usage of the URL outside of the AV Foundation framework is not supported.

Available in iOS 4.0 and later.

要はAV Foundation frameworkであるAVPlayer(AVQeuePlayer)で使える形に変換する必要があるってことですね。実際にはこんな感じ。

NSMutableArray *qItems;

for (MPMediaItem *mpItem in collection.items) {
	NSURL *url = [mpItem valueForProperty:MPMediaItemPropertyAssetURL];
	AVPlayerItem *api = [[AVPlayerItem alloc] initWithURL:url];
	[qItems addObject:api];
}

※collectionは、MPMediaItemCollection

ただこれでテストしていたら再生できない曲があって、いろいろ調べてみたら、MPMediaItemPropertyAssetURLがnullだった。

・昔、iTunesで購入したDRM付きの曲。
・iCloudにあって、iPhoneにダウンロードされていない曲。

この2つはURLがNULLになるらしい。

【参考】
blog.polikeiji.net: MPMediaItemPropertyAssetURLがnullのとき
iphone – How to detect if an MPMediaItem represents a DRM-protected audio track on iOS – Stack Overflow
ios4 – MPMediaItemPropertyAssetURL becomes null when Using MPMediaItems to play songs – Stack Overflow

で、こんな記事を見つけたので、再ダウンロードしてみたけど、DRMが外れない曲がある。まあ提供元が対応してなければ、そのままだよね。仮にDRMを外すことができたとしても、その行為をユーザにお願いするのはどうか?という問題もあるし。

iTunes Storeで購入したDRM楽曲は、再ダウンロードするとDRMフリーになる? | iPod love

ということで、この2種類は再生リストから外す方向で処理を考えてみます(続く)。

【Objective-C】AVPlayer関連リンク

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

とりあえずiPodライブラリを再生する音楽プレーヤー的なものを考えていたら、音の再生には3種類あるらしい。

・AVAudioPlayerクラス
・MPMusicPlayerControllerクラス
・AVPlayerクラス

【参考】Slow Days » Blog Archive » Core Audioざっくり覚え書き

今回考えているのは・・・

・バックグラウンド再生が可能(標準のミュージックプレイヤーと同じ)
・iPodライブラリ(自分がiPhoneに入れている音楽)の再生が可能

という前提なので、これだと「AVPlayerクラス」一択になるらしいので、関連リンクをメモしておきます。
(厳密には今回は、AVPlayerのサブクラスであるAVQueuePlayerを使っています)

Background Modes in iOS Tutorial
(これのPlaying Audioの箇所が役に立ちました)

かゆいところに手が届きそうなAVFoundationとMediaPlayerによる音楽再生アプリ作成メモ | Qiita
(ロック画面へアプリから情報表示など、知らなかったことばかり)

ios – Possible to insert item at top of queue using AVQueuePlayer? – Stack Overflow
(AVQueuePlayerで巻き戻しメソッドがないけどどうするの?キュー(Queue)なので、再生終わると配列から削除されます。で、原始的だけど、1つ前のAVPlayerItemを配列に入れて、順序を操作することで対応出来ます。ただ後ろに追加しかできないので、ちょっと面倒。)

AVPlayer Class Reference

AVQueuePlayer Class Reference

AVPlayerItem Class Reference

Objective-CのKVO(キー値監視)の使い方 | blog.nambo.jp

【2014.11.2 追記】
iOS の動画再生を試す | アカベコマイリ
(「AVPlayer の初期化」あたりは音楽でも同じ)

既にいくつか試してるのですが、その辺の記事は追々。

【Xcode】iTunes connectへのアプリ申請

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

昨日、無事アプリがリリースされました。パチパチ。「アイコン顔カメラ/icon face camera」というiPhoneアプリです。無料なので是非試してみて下さい。

アプリ申請でいくつか躓いたことがあったので、今後のためにメモ。
過去にもiOSアプリ制作に携わったことはあるのですが、リリース作業は今回が初めてでした。

・iTunes Connectは、iOS8リリースに合わせて更新された。
以前のと見た目が全然違っているので、ハッキリいって2014.8月以前の情報は無視した方がいいです。混乱します(笑)。

【参考】Xcode – 新しくなったiTunes Connectでのアプリの申請方法について – Qiita

・iAdはスクリーンショットに表示しない。
一週間経ってやっとレビューが始まったかと思ったら、数時間後にリジェクト(笑)。で、内容を確認してみると・・・

We found that your app and/or its metadata does not appear to include final content, which is not in compliance with the App Store Review Guidelines.

Specifically, your screenshots display empty iAd banners. It would be appropriate to remove the empty iAd banners from your screenshots.

で、訳すとこんな感じです。

私たちは、あなたのアプリケーションおよび/またはそのメタデータがApp Storeのレビューガイドラインを遵守していない最終的なコンテンツを含むように表示されないことがわかった。

具体的には、あなたのスクリーンショットは、空のiAdバナーを表示します。それはあなたのスクリーンショットから、空のiAdバナーを削除することが適切であろう。

iAd使ってるのにスクリーンショットに表示がないのはマズいと思って、わざわざ入れたのに完全に裏目でした。アプリ内の使っている画像もチェックして修正しました。

・アプリのバイナリのアップロードでエラー。
アーカイブを作って、Organizerからアップロードしたらエラーが出ました。

ERROR ITMS-9000: “Redundant Binary Upload. There already exists a binary upload with build version ‘1.0’ for train ‘1.0’”

調べてみたら、ビルド番号はアップロードごとに変更しないといけないらしいです。設定場所は、Xcodeの右カラムでプロジェクトを選んで、General>Identity>Buildです。デフォルトではVersionとBuildが同じになっていますが、Buildの番号を1.0.1などで変更すればいいらしいです。

【参考】ERROR ITMS-9000: “Redundant Binary Upload. There already exists a binary upload with build version ‘1.0’ for train ‘1.0’”のエラー対応方法

・iTunes connectでのビルド更新
新しくアップしたアプリ(バイナリ)は、マイAppのビルド欄に出てくるのかと思ってたら、マイAppのプレリリースタブの中に表示されるんですね。

・マイAppのビルド欄で「-」ボタン押して古いビルドを削除する。
・マイAppのビルド欄で「+」ボタン押して新しいビルドを選択する。

これで新しいビルドで再申請できました。

【参考】【アプリ申請】iTunes Connectが新しくなった!! 申請取り下げバイナリの再アップする方法

ちなみにiAdのスクリーンショット以外は問題(リジェクト)なかったのですが、申請からレビュー完了までかなり時間かかりました。iOS8のリリースと時期的に重なって、審査が混んでいたのかもしれませんが、参考までに書いておきます。

※申請時はリリース日付を2014.11.1に設定。尚、下記日時は米国時間です。

・September 28, 2014 22:34(c-geru)Prepare for Upload
・September 29, 2014 03:46(c-ger)Waiting For Review
・October 09, 2014 12:37(Apple)In Review
・October 09, 2014 19:09(Apple)Metadata Rejected >> iAdのスクリーンショットでリジェクト
・October 10, 2014 03:12(c-geru)Waiting For Review
・October 19, 2014 12:21(Apple)In Review
・October 19, 2014 13:02(Apple)Pending Developer Release >> レビュー完了
・October 20, 2014 09:25(c-geru)Processing for App Store >> リリース日付を10/21に変更
・October 20, 2014 09:31(Apple)Ready for Sale

【Objective-C】MPMediaQueryで取得される曲情報にはiCloudも含まれる

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

MPMusicPlayerControllerでミュージックライブラリの再生をしていて、一覧に出てくるのに再生できない曲があって「何だろう?プログラムの問題?」といろいろ調べていたら・・・

MPMediaQueryで取得される曲情報にはiCloudも含まれる

ということらしい。リストにはあるけど、曲データがないので再生できない。というか、[MPMusicPlayerController play] ではエラーにはならずに再生した途端に「MPMusicPlayerControllerPlaybackStateDidChangeNotification」が返ってきて、MPMediaItem == NULL で再生終了になる。

※問題の出たアルバムは1曲しか含まれていなかったので。

具体的には、MPMediaItem に MPMediaItemPropertyIsCloudItem というプロパティがあります。これでiCloudにあるのかどうか判定できます。

・MPMediaItemPropertyIsCloudItem
A Boolean value indicating whether the media item is an iCloud item (YES), or not (NO). A media item is considered an iCloud item if it is available via iTunes Match and is not already stored on the device. Value is an NSNumber object representing a BOOL data type.

Available in iOS 6.0 and later.

・元々のコードはこんな感じ。

MPMediaQuery *query = [MPMediaQuery albumsQuery];

NSArray *albumlists = query.collections;

for (MPMediaItemCollection *albumlist in albumlists) {
MPMediaItem *representativeItem = [albumlist representativeItem];
NSString *albumtTitle = [representativeItem valueForProperty:MPMediaItemPropertyAlbumTitle];
}

・で、調べてみたら、iCloudのデータを除外するには [MPMediaQuery addFilterPredicate:] ですればOK。

MPMediaQuery *query = [MPMediaQuery albumsQuery];

[query addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:[NSNumber numberWithBool:NO] forProperty:MPMediaItemPropertyIsCloudItem]];

NSArray *albumlists = query.collections;

for (MPMediaItemCollection *albumlist in albumlists) {
MPMediaItem *representativeItem = [albumlist representativeItem];
NSString *albumtTitle = [representativeItem valueForProperty:MPMediaItemPropertyAlbumTitle];
}

問題の出た曲データが特定できなくて、iOS純正のミュージックプレイヤーと自作アプリの曲アルバム一覧を比較していて、やっと気づきました。

【参考】
MPMediaItem Class Reference | iOS Developer Library
MPMediaQuary albumsQuery結果からiCloudのみにあるものを除く | 響雲
articles of samekard: iPod library access クラウド音源への対応
MPMediaQuery – ignore songs on Cloud (i.e., iTunes Match)