NSUbiquitousKeyValueStoreを使用していくつかのアプリ設定を保存しています。私の論理は次のとおりです。データをローカルに保存するときは、バックアップとしてもNSUbiquitousKeyValueStoreに保存します。設定が必要な場合は、ローカルで読み取り、ローカルでデータが見つからない場合(たとえば、アプリを再インストールした後)にのみiCloudKey-Valueストアを使用します。ユーザーが1つのicloudIDを共有する複数のデバイスを持っている場合、ユーザーは1つのデバイスに設定を書き込んで、別のデバイスにダウンロードできます(書き換えについて警告します)。
奇妙な問題があります。手順:
- アプリをインストールし、そのデータをNSUbiquitousKeyValueStoreに保存します。データがそこにあることを確認しました。
- アプリを削除しました(データがまだiCloudに残っていると仮定します)。
- 念のため数分待ってから、Xcode内からアプリをインストールして起動しました。
- [[NSUbiquitousKeyValueStore defaultStore] dataForKey:@ "mykeyname"]を使用して設定キーを読み取ろうとしました-問題ない場合もありますが、キーが見つからない場合もあります。
- 15秒間待ってから、再試行しました。成功。混乱している。
したがって、iOSは、私のアプリのリモートキー値ストレージをdataForKey:呼び出しでローカルに使用できるようにするために時間がかかるようです。もし私がそのようなシステムを書いたとしたら(実際に私は書いた-しばらく前に、別の人生で)、キー値データを要求して受け取る前に明らかに遅れがなければならない。そこで、「最初の起動時にキー値ストレージのダウンロード/同期を終了しました」などの通知を受け取りたいと思います。
私が理解している限り、メインスレッドでNSUbiquitousKeyValueStoreを同期的に操作できます(これは私にとって便利です)。しかし、[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]は有効なURLを返し、「キーが見つかりません」というメッセージが表示されます。だから私はそれに頼ることができません。NSUbiquitousKeyValueStoreが機能し、ダウンロードされていることを確認する方法はありますか?特に遅いインターネットでは重要です。
アップデート
[[NSUbiquitousKeyValueStore defaultStore] synchronize](Appleドキュメントに記載されている)をinitとloadに追加すると、少し役に立ちました。それでもiCloudには多くの質問があります。
昨日、電話1のKey-Valueストアにデータを正常に保存し、電話2で復元しました。今日、電話2のアプリを削除し、データを復元しようとしました。しかし、[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]でも有効なURLが返され、[[NSUbiquitousKeyValueStore defaultStore] synchronize]を呼び出しました。dataForKey:MY_DATA_KEYを呼び出すとnilになります。
電話1でicloudからデータを復元しようとすると(アプリはまだインストールされています)、成功しますが、この電話に再インストールすると、アプリの復元は成功しなくなります。
一時的な解決策は、「iCloudをオフにする-> Documents&Data-ネットワークをオフにしてからオンにする-Documents&Dataをオンにする」ですが、数分待ってから動作するはずです。
だから、質問:
- iCloudでそのような問題がありますか?
- データが利用できないか、まだダウンロードされていないかを確認する方法はありますか?
- iCloudの既知の「レイテンシ」はありますか?私は約7秒聞いたが、それは明らかに真実ではない。
- アプリがインストールされていない場合、iCloudデータの更新はかなり高速(秒)のようですが、アプリを再インストールすると、iCloudはKey-Valueストアを実現するのに数分かかります。このプロセスを強制する方法はありますか?
PS 以下は、参考のために私のCloudHelperです-iCloudKey-Valueストアとの間でバイナリデータを読み書きするための非常に単純なc++クラスです。コンパイル可能ではありません。より明確にするために、SOに合わせて調整しました。エンジン関連のコードを削除しました。それでも、MySystem :: ...呼び出しを削除すると、かなりうまく機能します。私が前に述べたことを除いて。
class CloudHelper
{
public:
static bool init();
static void deInit();
//save our data to iCloud with
static int saveData(unsigned char* data, int from, int count);
//get our data from iCloud
static unsigned char * loadData(int *retsize, int * retint);
//does iCloud work for us
static bool isEnabled();
//do we have our key in iCloud
static int isAvailable();
static const int RESULT_OK = 0;
static const int RESULT_NO_CONNECTION = 1;
static const int RESULT_NOT_FOUND = 2;
static const int RESULT_SYNC_ERROR = 3;
private:
static bool enabled;
static NSURL *ubiq;
};
bool CloudHelper::enabled = false;
NSURL *CloudHelper::ubiq = NULL;
#define MY_DATA_KEY @"my_data_key"
int CloudHelper::saveData(unsigned char* data, int from, int count)
{
if ([NSUbiquitousKeyValueStore defaultStore])
{
NSData *d = [[[NSData alloc] initWithBytes:(data + from) length:count] autorelease];
[[NSUbiquitousKeyValueStore defaultStore] setData:d forKey: MY_DATA_KEY)];
if ([[NSUbiquitousKeyValueStore defaultStore] synchronize] != TRUE)
return RESULT_SYNC_ERROR;
return RESULT_OK;
}
return RESULT_NO_CONNECTION;
}
unsigned char * CloudHelper::loadData(int *retsize, int * retint)
{
if ([NSUbiquitousKeyValueStore defaultStore])
{
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
NSData *d = [[NSUbiquitousKeyValueStore defaultStore] dataForKey: MY_DATA_KEY];
if (d != NULL)
{
if (retsize != NULL)
*retsize = d.length;
if (retint != NULL)
*retint = RESULT_OK;
return d.bytes;
}
else
{
if (retsize != NULL)
*retsize = -1;
if (retint != NULL)
*retint = RESULT_NOT_FOUND;
}
}
else
{
if (retsize != NULL)
*retsize = -1;
if (retint != NULL)
*retint = RESULT_NO_CONNECTION;
}
return NULL;
}
int CloudHelper::isAvailable()
{
int result = RESULT_NO_CONNECTION;
if ([NSUbiquitousKeyValueStore defaultStore])
{
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
NSData *d = [[NSUbiquitousKeyValueStore defaultStore] dataForKey: MY_DATA_KEY];
if (d != NULL)
result = RESULT_OK;
else
result = RESULT_NOT_FOUND;
}
else
result = RESULT_NO_CONNECTION;
return result;
}
void CloudHelper::deInit()
{
enabled = false;
[ubiq release];
}
bool CloudHelper::init()
{
enabled = false;
NSURL *ubiq_ = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
if (ubiq)
{
enabled = true;
ubiq = [ubiq_ retain]; //save for further use
}
else
{
//is implemented elsewhere: this writes a local file with a counter, and if it is < REMINDER_COUNT allows us to show a warning to users
bool allow = MySystem::isAllowToShowDialog();
if (allow)
{
//determines network state with Apple's Reachability
if (!MySystem::isNetworkAvailable())
MySystem::showMessageBox(@"Network error"); //No network
else
MySystem::showMessageBox(@"You should log into your iCloud account to be able to backup your settings."); //No login
}
}
return enabled;
}
更新2
2016年です。AndroidはiOSの邪悪な双子になり、人類は重力波を発見し、Higgsは彼のノーベルを受け取り、MicrosoftはNokiaを買収して殺害しました。しかし、iCloudはまだそれがそうであったのと同じくらい愚かです。
最後に、いくつかのVPSで独自のネットワークサービスのスタックを作成しました。サードパーティのサービスのほとんどは不安定で予測できないため、使用を拒否しました。それでも私はiCloudが必要です。アップルのもう一人の死んだ子供は働かないからです。SecKeyChain。私のゲームが始まると、そのサービスは停止します。そこで、再インストール後もユーザーを区別するためにランダムなUUIDをクラウドに保存することにしました(デバイスIDはもうありません)。しかし、何がうまくいかない可能性がありますか?すべての!このばかげたs*itをエラーなしでデプロイするために2日間を費やしましたが、今ではデータが失われることがあります。
ありがとうアップル、ありがとう、ありがとう、ありがとう!ラララ!ヒップヒップフーレイ!(サーカス音楽の音、しだれに消えていく)