忍者ブログ
[1] [2] [3] [4] [5] [6] [7] [8
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。


先月書いたcoredataありの時のフィル共有の追加メモ。

前回書いた分へのリンク

前回の最後に書いていた「バージョンアップでファイル共有に対応する」部分についてとりあえず対応が出来た。
やった事は、ものすごく単純な方法で問題なさそうであった。

手順として、以下のようにした。
1.データベースファイルの保存場所が、共有される場所にあるかのチェック
2.ファイルがある場合には、別の場所にファイル移動

当たり前の事をしただけだった。

実際のソースは以下のようにした。

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }
    
    NSURL *oldStoreURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Test.sqlite"];
    NSURL *storeURL = [NSURL fileURLWithPath: [[self applicationSupportDirectory] stringByAppendingPathComponent: @"Test.sqlite"]];
    NSFileManager* fileManager = [NSFileManager defaultManager];
    NSError *error = nil;
    NSString *oldString = [oldStoreURL path];
    NSString *newString = [storeURL path];

    if([fileManager fileExistsAtPath:oldString])
    {
        if (![fileManager moveItemAtPath:oldString toPath:newString error:&error]) {
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }

        NSString *oldString2 = [oldString stringByAppendingString:@"-shm"];
        NSString *newString2 = [newString stringByAppendingString:@"-shm"];
        if (![fileManager moveItemAtPath:oldString2 toPath:newString2 error:&error]) {
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }

        oldString2 = [oldString stringByAppendingString:@"-wal"];
        newString2 = [newString stringByAppendingString:@"-wal"];
        if (![fileManager moveItemAtPath:oldString2 toPath:newString2 error:&error]) {
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }

    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }    
    
    return _persistentStoreCoordinator;
}

「AppDelegate.m」を修正。
元々のファイルの保存先を指定して、ファイルの有無チェックを行い、ファイルがあった場合のみファイル移動を行う。
このとき、coredataのデータベースファイルの3つを全て移動対象にする。

たったこれだけの、単純な実装ですんでよかった。


拍手[0回]

PR

iOSアプリでは、iTunesを利用してPCとファイル共有する事が出来る。
当然、開発時に設定を行う事で、この機能が使えるアプリを作成する事が出来る。

まずは、この共有方法から。

これは、すごく簡単で、「XXXX-Info.plist」のファイルにKeyを1つだけ追加する事で可能となる。
Keyに「Application supports iTunes file sharing」を追加して、これを「YES」に変更するだけでファイル共有が可能になる。

さて、ここからが今回のタイトルのお話。

上記の手順でファイル共有を有効にすると、iTunesの「App」のタブにあるファいつ共有の部分にアプリが追加される。
しかし、このアプリがcoredataを使用しているアプリであれば、共有されているファイル一覧の中に、データベースのsqliteファイルが見える。

これは、何かと面倒な事が発生する可能性が高いため、見えないようにする必要がある。
その方法として、sqliteファイルの置き場所を変える方法があるらしい。
保村先として妥当な箇所等も含め調べた所、「/Library/Application Support」がよいらしい。

今回、そのまま解説してあるページがあったので、そこを参考にした。
参考ページ

基本的にここに書かれているソースをそのまま使用する事で、問題なく動作した。
(自分の場合、分かりやすくしたかったので、メソッド化した。)

今回開発中のアプリに関しては、これで、問題なく対応する事が出来たのだが、既にリリースしたアプリで問題になるかもしれない。
それは、coredataを使用したアプリなのだが、今の所、ファイル共有を有効にしていない。
その為、データベースの保存先の変更も行っていない。

バージョンアップで、ファイル共有を有効にする事を考えているので、その場合、どのように管理するのか考える必要がある。
直に対応する訳ではないので、じっくり考えて試しながら対応を行っていく予定。

これに関して、よい対応が出来た時には、ここに残したい。

拍手[0回]


xcodeを最新の環境の5.1にバージョンアップしてから、初めてadmobを入れようとしては待ってしまった。

admob広告表示に関しては、一度実装してみた後は、コピペですんでいた。
今回も、今までと同じように前回作成したアプリのソースをコピペした所、エラーになった。

エラーの内容は、以下のようなものが出てきた。
===================================================================
ld: warning: ld: warning: ld: warning: ignoring file /ファイルのパス/adkit/libAdapterIAd.a, missing required architecture arm64 in file /ファイルのパス/adkit/libAdapterIAd.a (3 slices)ignoring file /ファイルのパス/libAdapterNend.a, missing required architecture arm64 in file /ファイルのパス/adkit/libAdapterNend.a (3 slices)ignoring file /ファイルのパス/adkit/libGoogleAdMobAds.a, missing required architecture arm64 in file /ファイルのパス/libGoogleAdMobAds.a (3 slices) 
Undefined symbols for architecture arm64:
  "_OBJC_CLASS_$_GADRequest", referenced from:
      objc-class-ref in CategoryEditViewController.o
      objc-class-ref in MainViewController.o
      objc-class-ref in ItemEditViewController.o
  "_kGADAdSizeBanner", referenced from:
      -[CategoryEditViewController viewDidLoad] in CategoryEditViewController.o
      -[MainViewController viewDidLoad] in MainViewController.o
      -[ItemEditViewController viewDidLoad] in ItemEditViewController.o
  "_OBJC_CLASS_$_GADBannerView", referenced from:
      objc-class-ref in CategoryEditViewController.o
      objc-class-ref in MainViewController.o
      objc-class-ref in ItemEditViewController.o
  "_kGADAdSizeLeaderboard", referenced from:
      -[CategoryEditViewController viewDidLoad] in CategoryEditViewController.o
      -[MainViewController viewDidLoad] in MainViewController.o
      -[ItemEditViewController viewDidLoad] in ItemEditViewController.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
===================================================================
このブログでも以前に書いていた部分に引っかかったのかと思い見直してみたが、ちょっと違う。

以前は、単純にビルドの設定を変更する事で対応できたのだが、今回は、前回と同じ選択肢が存在しない。
それに、他にもちょっと怪しい指摘もあるので、全体的に見直しを行う事にした。

まずは、組み込むadmonやAppBankのファイルのバージョンをチェック。
案の定、既に古いものを使用していた。

一度、使い始めた後に、この部分に関して、あまりバージョンアップをしていなかった。
早速、全て、最新のファイルを取得し組み込むがあまり変わらない。

というよりも、エラーが増えた。
===================================================================
  "_AVAudioSessionPortBuiltInSpeaker", referenced from:
      -[GADDevice audioRouteUsingAVAudioSession] in libGoogleAdMobAds.a(GADDevice.o)
  "_AVAudioSessionPortHeadphones", referenced from:
      -[GADDevice audioRouteUsingAVAudioSession] in libGoogleAdMobAds.a(GADDevice.o)
  "_OBJC_CLASS_$_AVAudioSession", referenced from:
      objc-class-ref in libGoogleAdMobAds.a(GADDevice.o)
  "_OBJC_CLASS_$_CTTelephonyNetworkInfo", referenced from:
      objc-class-ref in libGoogleAdMobAds.a(GADDevice.o)
===================================================================
こんなのが増えている。

これは、中をじっくり見ると、どうも、必要なframeworkが増えているようだった。
結果的に、以前使っていた時よりも、下記の2個が必要なっていた。
・AVFoundation.framework
・CoreTelephony.framework

この追加で、とりあえず、エラーはなくなったが、64bitでビルドするとワーニングが残る。
そのまま実行すると、AppBankの広告は表示されない。
ログに広告の取得失敗が残っている。
これこそ、以前と同じなのだが、ビルドの設定に以前と同じものはなくなっている。

しかたなく、「Build Setting」の「Architectures」を「Other」を選択。
そこで、「armv7」「armv7s」の2項目を入力し、他の項目は削除しておく。
この設定で、ワーニングは消えた。
動作も、問題なく広告表示されるようになったので、これでよいと思われる。

以前のバージョンでは、選択肢にあったので簡単であったのに、今回は、手入力が必要になったようだ。

拍手[1回]


前回に続き、Bluetoothの通信についての内容。

前回記載した対応で、通信の確立を行い、簡単なデータの送受信(短い文字列)の確認まで出来ていた。
ここから、今回開発するデータの形式にあわせてカスタマイズしていくのだが、また引っかかってしまった。

ある程度、送受信するデータの整形を実装し、何度かテストを行っていると、直に接続が切れてしまう事があった。
特に、切断理由が出る訳でもないため、意味が分からなかった。

何度かパターンをかえながらテストを行っている時に今回の実装の為に調べた中で思い出した事があった。

GameKitのGKSessionやGKPeerPickerControllerを使った場合、データサイズが1000バイトまでが推奨サイズらしい。
さらに、87Kbの制限があるとの記載まである。
しかし、このサイズを超えても、動作したと報告もあったので、その時にはあまり気にしなかった。
しかし、今回確認した内容を考えた場合、データサイズの制限に引っかかっている可能性が高い。

仕方なく、データを分割して通信を行う事にした。
何とも面倒な事だが仕方がない。
まず始めに、推奨サイズに指定されている1000バイトで実装を進める。
(当然、サイズはデファイン化し、最終的には、バランスのよいサイズを探る予定)

まずは、送信側でNSData型にまとめたデータを分割する。

    NSRange dataRange;
    NSInteger dataSize = pinData.length;
    NSInteger dataSplitCount = dataSize/DATA_SPLIT_SIZE;

    dataRange.length = DATA_SPLIT_SIZE;
    dataRange.location = 0;
    NSMutableArray *dataArray = [NSMutableArray array];
    for(NSInteger cnt = 0;cnt<dataSplitCount;cnt++)
    {
        [dataArray addObject:[sendData subdataWithRange:dataRange]];
        dataRange.location += DATA_SPLIT_SIZE;
    }
    // 最後のデータは、レングスを調整してから処理を行う
    dataRange.length = dataSize%DATA_SPLIT_SIZE;
    [dataArray addObject:[pinData subdataWithRange:dataRange]];

このようにして、データの分割を行う。
そして、受信側は、これの反対の処理を行う

    NSMutableData *receiveData;

これで、保存エリアを確保した状態で、下記のように受信データをつなげていく。
    // 受信データを保存する
    [receiveData appendData:data];

これでを繰り返す事で、大きなデータに戻す事が出来る。
この、「NSMutableData 」は、NSDataとしてそのまま使用する事が可能。
その為、最後のデータを受信してから下記処理を行う事で元のデータ構造に戻す事が出来る。

    DataObject *receiveObject = [NSKeyedUnarchiver unarchiveObjectWithData: receiveData];

この実装で動作確認をすると、問題なくデータの送受信が出来た。
何とも面倒な処理を書く必要があった。

しかし、逆に大きなデータの転送を行う場合には、分割したいくつ目まで送受信を行ったのかを監視するとこで、進捗状況を表示する事が出来る。
この辺りをうまく整理して実装する事で、見た目のよいものを作る事が出来ると思う。

あとは、最初の方に書いているように、1回のデータ送受信のサイズを、安定性、スピード等を考えて、いろいろ試しながら調整する必要があると思われる。

拍手[0回]


今開発しているアプリで、Bluetoothを使用して、通信を行うものを入れようとしている。
この実装には、かなり苦労してしまったので、残す事にする。

Bluetoothは使った事が無かったので、いつも通りにネットで検索して、実装方法を参考にしていた。

まず始めに、「GameKit.framework」の「GKPeerPickerController」を使う事で実装した。

とりあえず動かしてみると、中途半端に動くので、調べてみると、「GKPeerPickerController」は、iOS7は未サポートであり、現在は、非推奨となっている。
これに変わるものとして、「MultipeerConnectivity.framework」が必要な、「MCBrowserViewController」「MCSession」を使用する方法になっていた。

これに関しても、ネットで検索すると、サンプルソースとともに、解説がいくつもあったので、分かりやすいと思うページを参考にするとよいと思う。

早速、これに置き換えようとして、解説を読んでいくと、新しい「MCBrowserViewController」は、iOS7からしか使用できないと書かれている。
仕方ないと思い、OSのバージョンを見て切り分けるように実装し、動作確認してみた。

すると、通信相手を検索にいくのだが、全く発見できない。
「GKPeerPickerController」をiOS7で使った時には、検索する事は出来ていたのに・・・

いろいろ試しながら、調べていくと、どうやら、「GKPeerPickerController」と「MCBrowserViewController」では、通信できないらしい。
これでは、はっきり言って使い物にならないのではないかと思ってしまう。
今回(いつも同じだが)、iOS6とiOS7の実機で確認するようにはしている。
特に今回は、2台で通信するので、それぞれ、別のOSの実機で確認していたから、すぐに気がついた。
一通り作り込んだ後に確認していたら、とんでもない後戻りになったと思う。
その点は、運がよかったのかも・・・

しかし、この事に関しては、あまり情報がなく、ほとんどが、それぞれの方法についての解説のみだった。
iOS6、iOS7の両方をサポートしたアプリを作ることは無理なのかとも思い始めていたが、やっと方法が見つかった。

iOS7の場合、「GKPeerPickerController」は、中途半端に動作するという事がポイント。
具体的には、接続状態が変更された時に呼ばれる「- (void)session:(id)session peer:(id)peerID didChangeState:(GKPeerConnectionState)state」が、切断された時にしか動かない。
この為、接続が確立した事が分からなくなってしまっているのだ。

そこで、対策として、接続処理が終わった時にコールされる「- (void)peerPickerController:(GKPeerPickerController *)picker didConnectPeer:(NSString *)peerID toSession:(GKSession *)session」で、iOS7の場合には、強制的に、「- (void)session:(id)session peer:(id)peerID didChangeState:(GKPeerConnectionState)state」をコールしてしまうと動かす事が出来た。

実際のソースは、下記のイメージ

- (void)peerPickerController:(GKPeerPickerController *)picker didConnectPeer:(NSString *)peerID toSession:(GKSession *)session
{
    // セッションを保管
    currentSession = session;
    // デリゲートのセット
    session.delegate = self;
    // データ受信時のハンドラを設定
    [session setDataReceiveHandler:self withContext:nil];
    
    // ピアピッカーを閉じる
    picker.delegate = nil;
    [picker dismiss];
    
    if (osVersion >= 7.0) {
        // iOS7.0
        if (self.isConnecting == NO)
            [self session:currentSession peer:peerID didChangeState:GKPeerStateConnected];
    }
}

- (void)session:(id)session peer:(id)peerID didChangeState:(GKPeerConnectionState)state
{
    switch (state) {
        case GKPeerStateAvailable:
            // (省略)
            break;
        case GKPeerStateConnecting:
            // (省略)
            break;
        case GKPeerStateConnected:
            // 接続完了
            self.isConnecting = YES;
            // 接続完了後の処理をここに書く(省略)
            break;
        case GKPeerStateDisconnected:
            // (省略)
            break;
        case GKPeerStateUnavailable:
            // (省略)
            break;
        default:
            break;
    }
}

ポイントとなる「GKPeerStateConnected」の部分以外は基本的に省略しているが、上記のような実装で、うまく接続を確立して通信する事が出来た。

ただ、非推奨となっていて、iOS7では、未サポートとされるものを使用して、無理矢理動かした感が強いと思う。
そのため、どんな不具合が出るのか、今は予想できていない。
さらに、Appleの審査で問題とならないのかもよくわからない。

最悪の場合、iOS7専用にしてしまうと思うが、今の所は、このまま開発を進める。
(知り合いにも、iOS7が気に入らないからとアップデートしない人は、数人いるので、まだ、iOS6もサポートしたいと思う。)

最終的に、テスト、アプリ申請と進める上で、問題が出たら(問題なく完了しても)、ここに残す予定。

拍手[3回]


リリース済みアプリ

Silmuvide


PseudoRPT


うつすと!


ChronologyMaker


LASI


3行日記


単位計算機


Marking Map Plus


交通費管理


交通費計算リスト


Markin Map


SheepSleepSheep


RootCalculator


元号変換


PieChart


MeasureShooting


SimpleMapCreator



Applivのレビュー記事
http://app-liv.jp/713163900/

ゆびてんじ


PR



Copyright ©   marble seijin の開発日記   All Rights Reserved
Design by MMIT simple_plain Powered by NINJA TOOLS
忍者ブログ [PR]