Xamarin.iOS の CloudKit
CloudKit フレームワークは、iCloud にアクセスするアプリケーションの開発を効率化します。 これには、アプリケーション データと資産権限の取得と、アプリケーション情報を安全に格納できることが含まれます。 このキットは、個人情報を共有することなく、iCloud ID を使用してアプリケーションにアクセスできるようにすることで、ユーザーに匿名性を提供します。
開発者はクライアント側のアプリケーションに集中でき、iCloud でサーバー側のアプリケーション ロジックを記述する必要がなくなります。 CloudKit は、認証、プライベート データベースとパブリック データベース、構造化データおよび資産ストレージ サービスを提供します。
重要
Apple からは、開発者が欧州連合の一般データ保護規則 (GDPR) を適切に処理するためのツールが提供されています。
要件
この記事で説明する手順を完了するには、次のものが必要です:
- Xcode と iOS SDK – Apple の Xcode および iOS 8 API を開発者のコンピューターにインストールして構成する必要があります。
- Visual Studio for Mac – 最新バージョンの Visual Studio for Mac をユーザー デバイスにインストールして構成する必要があります。
- iOS 8 デバイス – テスト用に iOS8 の最新バージョンを実行している iOS デバイス。
CloudKit とは?
CloudKit は、開発者に iCloud サーバーへのアクセス権を付与する方法です。 これは、iCloud ドライブと iCloud フォト ライブラリの両方の基盤を提供します。 CloudKit は、macOS デバイスと iOS デバイスの両方でサポートされています。
CloudKit は iCloud アカウント インフラストラクチャを使用します。 デバイス上の iCloud アカウントにログインしているユーザーがいる場合、CloudKit は ID を使用してユーザーを識別します。 使用できるアカウントがない場合は、制限付きの読み取り専用アクセスが提供されます。
CloudKit では、パブリック データベースとプライベート データベースの両方の概念がサポートされています。 パブリック データベースは、ユーザーがアクセスできるすべてのデータの "スープ" を提供します。 プライベート データベースは、特定のユーザーにバインドされたプライベート データを格納するためのものです。
CloudKit では、構造化データと一括データの両方がサポートされます。 大きなファイル転送をシームレスに処理できます。 CloudKit は、バックグラウンドで iCloud サーバーとの間で大きなファイルを効率的に転送し、開発者が他のタスクに集中できるようにします。
Note
CloudKit は トランスポート テクノロジであることに注意してください。 永続化は提供されません。アプリケーションがサーバーからの情報を効率的に送受信できるようになるだけです。
この記事を書いている時点では、Apple は当初、帯域幅とストレージ容量の両方に上限を設けて CloudKit を無料で提供しています。 大規模なプロジェクトや大規模なユーザー ベースのアプリケーションに対しては、手頃な価格の価格スケールが提供されることを Apple は示唆しています。
Xamarin アプリケーションでの CloudKit の有効化
Xamarin アプリケーションで CloudKit フレームワークを利用するには、「機能の使用」ガイドと「エンタイトルメントの操作」ガイドで詳しく説明されているように、アプリケーションを正しくプロビジョニングする必要があります。
CloudKit にアクセスするには、Entitlements.plist ファイルに、iCloud の有効化、キー値ストレージ、CloudKit のアクセス許可が含まれている必要があります。
サンプル アプリ
サンプル アプリでは、Xamarin で CloudKit を使用する方法を示します。 以下の手順では、サンプル を構成する方法を示します。これには CloudKit 自体に必要なもの以外の追加の設定が必要です:
- Visual Studio for Mac または Visual Studio でプロジェクトを開きます。
- ソリューション エクスプローラーで、Info.plist ファイルを開き、バンドル識別子が、プロビジョニングのセットアップの一部として作成されたアプリ ID で定義されたものと一致することを確認します。
- Info.plist ファイルの一番下までスクロールし、[有効なバックグラウンド モード]、[場所の更新]、[リモート通知] を選択します。
- ソリューションで iOS プロジェクトを右クリックし、[オプション] 選択します。
- [iOS バンドル署名] 選択し、[開発者 ID] と上記で作成したプロビジョニング プロファイルを選択します。
- Entitlements.plist に、iCloudの有効化、キー値ストレージと CloudKit が含まれていることを確認します。
- アプリケーションの Ubiquity Container が存在することを確認します。 例:
iCloud.com.your-company.CloudKitAtlas
- 変更をファイルに保存します。
これらの設定で、サンプル アプリは、CloudKit Framework API だけでなく、バックグラウンド、場所、および通知サービスにもアクセスできるようになりました。
CloudKit API の概要
Xamarin iOS アプリケーションで CloudKit を実装する前に、この記事では、次のトピックを含む CloudKit Framework の基礎について説明します:
- コンテナー – iCloud 通信の分離されたサイロです。
- データベース – パブリックとプライベートをアプリケーションで使用できます。
- レコード – 構造化データが CloudKit との間で移動されるメカニズム。
- レコード ゾーン – レコードのグループです。
- レコード識別子 – 完全に正規化されており、レコードの特定の場所を表します。
- リファレンス 特定の–データベース内の関連レコード間の親子関係を提供します。
- 資産 – iCloud にアップロードされ、特定のレコードに関連付けられる大規模な非構造化データのファイルを許可します。
Containers
iOS デバイスで実行されている特定のアプリケーションは、そのデバイス上の他のアプリケーションやサービスと共に常に実行されます。 クライアント デバイスでは、何らかの方法でアプリケーションがサイロ化またはサンドボックス化されます。 これはリテラル サンドボックスの場合もあり、アプリケーションが単に独自のメモリ空間で実行される場合もあります。
クライアント アプリケーションを取得し、他のクライアントから分離して実行する概念は非常に強力であり、次の利点があります:
- セキュリティ – 1 つのアプリケーションが他のクライアント アプリや OS 自体に干渉することはできません。
- 安定性 – クライアント アプリケーションがクラッシュした場合、OS の他のアプリを取り出すことはできません。
- プライバシー – 各クライアント アプリケーションは、デバイス内に格納されている個人情報へのアクセスが制限されています。
CloudKit は、上記と同じ利点を提供し、それらをクラウドベースの情報の操作に適用するように設計されています:
アプリケーションがデバイス上で動作する one-of-many であるように、アプリケーションと iCloud との通信も one-of-many です。 これらの異なる通信サイロはそれぞれコンテナーと呼ばれます。
コンテナーは、CKContainer
クラスを介して CloudKit Framework で公開されます。 既定では、1 つのアプリケーションが 1 つのコンテナーと通信し、このコンテナーはそのアプリケーション用のデータを分離します。 これは、複数のアプリケーションが同じ iCloud アカウントに情報を格納できることを意味しますが、この情報は混ざり合うことはありません。
iCloud データのコンテナー化により、CloudKit でユーザー情報をカプセル化することもできます。 このようにして、アプリケーションは iCloud アカウントとその中に保存されているユーザー情報へのアクセスをある程度制限することができ、同時にユーザーのプライバシーとセキュリティを保護することができます。
コンテナーは、アプリケーションの開発者が WWDR ポータルを介して完全に管理されます。 コンテナーの名前空間はすべての Apple 開発者に対してグローバルであるため、コンテナーは特定の開発者のアプリケーションだけでなく、すべての Apple 開発者とアプリケーションに固有である必要があります。
Apple では、アプリケーション コンテナーの名前空間を作成するときに逆引き DNS 表記を使用することを提案しています。 例: iCloud.com.company-name.application-name
コンテナーは、既定では特定のアプリケーションに 1 対 1 でバインドされていますが、アプリケーション間で共有できます。 そのため、複数のアプリケーションを 1 つのコンテナーで調整できます。 1 つのアプリケーションが複数のコンテナーと通信することもできます。
データベース
CloudKit の主な機能の 1 つは、iCloud サーバーまでモデル化するアプリケーションのデータ モデルとレプリケーションを取得することです。 一部の情報は、それを作成したユーザーを対象としており、他の情報は、ユーザーが一般に使用するために作成できる公開データ (レストラン レビューなど) であるか、開発者がアプリケーション用に公開した情報です。 どちらの場合も、対象ユーザーは単一のユーザーではなく、人々のコミュニティです。
コンテナー内では、まず何よりもパブリック データベースを使用します。 ここで、すべての公開情報が存在し、共同で混在します。 さらに、アプリケーションのユーザーごとに複数の個別のプライベート データベースがあります。
iOS デバイスで実行している場合、アプリケーションは現在ログオンしている iCloud ユーザーの情報にのみアクセスできます。 そのため、アプリケーションのコンテナーのビューは次のようになります:
現在ログオンしている iCloud ユーザーに関連付けられているパブリック データベースとプライベート データベースのみを表示できます。
データベースは、CKDatabase
クラスを介して CloudKit Framework で公開されます。 すべてのアプリケーションは、パブリック データベースとプライベート データベースの 2 つのデータベースにアクセスできます。
コンテナーは、CloudKit への最初のエントリ ポイントです。 次のコードを使用して、アプリケーションの既定のコンテナーからパブリック データベースとプライベート データベースにアクセスできます:
using CloudKit;
//...
public CKDatabase PublicDatabase { get; set; }
public CKDatabase PrivateDatabase { get; set; }
//...
// Get the default public and private databases for
// the application
PublicDatabase = CKContainer.DefaultContainer.PublicCloudDatabase;
PrivateDatabase = CKContainer.DefaultContainer.PrivateCloudDatabase;
データベースの種類の違いを次に示します:
パブリック データベース | プライベート データベース | |
---|---|---|
データの種類 | 共有データ | 現在のユーザーのデータ |
売上予算 | 開発者のクォータで考慮される | ユーザーのクォータで考慮される |
既定のアクセス許可 | 全ユーザー読み取り可能 | ユーザー読み取り可能 |
権限の編集 | レコード クラス レベルを介する iCloud ダッシュボード ロール | 該当なし |
レコード
コンテナーはデータベースを保持し、データベース内にはレコードがあります。 レコードは、構造化データが CloudKit との間で移動されるメカニズムです:
レコードは、キーと値のペアをラップする CKRecord
クラスを介して CloudKit Framework で公開されます。 アプリケーション内のオブジェクトのインスタンスは、CloudKit の CKRecord
と同じです。 さらに、各 CKRecord
には、オブジェクトのクラスに相当するレコード型があります。
レコードには Just-In-Time スキーマがあるため、データは処理のために渡される前に CloudKit に記述されます。 その時点から、CloudKit は情報を解釈し、レコードの格納と取得のロジスティクスを処理します。
CKRecord
クラスでは、さまざまなメタデータもサポートされています。 たとえば、レコードには、レコードが作成された日時と、レコードを作成したユーザーに関する情報が含まれます。 レコードには、最後に変更された日時と、そのレコードを変更したユーザーに関する情報も含まれます。
レコードには、Change Tag の概念が含まれています。 これは、特定のレコードのリビジョンの以前のバージョンです。 Change Tag は、クライアントとサーバーが特定のレコードの同じバージョンを持っているかどうかを判断するための軽量な方法として使用されます。
前述のように、CKRecords
はキーと値のペアをラップするので、次の種類のデータはレコードに格納できます:
NSString
NSNumber
NSData
NSDate
CLLocation
CKReferences
CKAssets
レコードには、単一の値の型に加えて、上記のいずれかの型の同種配列を含めることができます。
次のコードを使用して、新しいレコードを作成し、データベースに格納できます:
using CloudKit;
//...
private const string ReferenceItemRecordName = "ReferenceItems";
//...
var newRecord = new CKRecord (ReferenceItemRecordName);
newRecord ["name"] = (NSString)nameTextField.Text;
await CloudManager.SaveAsync (newRecord);
レコード ゾーン
レコードは特定のデータベース内に単独で存在せず、レコード ゾーン内にレコードのグループとして一緒に存在します。 レコード ゾーンは、従来のリレーショナル データベースではテーブルと考えることができます:
特定のレコード ゾーン内に複数のレコードがあり、特定のデータベース内に複数のレコード ゾーンを含めることができます。 すべてのデータベースには、既定のレコード ゾーンが含まれています:
これは、レコードが既定で格納される場所です。 さらに、カスタム レコード ゾーンを作成できます。 レコード ゾーンは、アトミック コミットと Change Tracking が行われる基本粒度を表します。
レコード識別子
レコード識別子は、クライアントが指定したレコード名とレコードが存在するゾーンの両方を含むタプルとして表されます。 レコード識別子には、次の特性があります:
- これらはクライアント アプリケーションによって作成されます。
- 完全に正規化されており、レコードの特定の場所を表します。
- 外部データベースのレコードの一意の ID をレコード名に割り当てて、CloudKit 内に格納されていないローカル データベースをブリッジのに使用できます。
開発者は、新しいレコードを作成するときに、レコード識別子を渡すように選択できます。 レコード識別子が指定されていない場合、UUID が自動的に作成され、レコードに割り当てられます。
開発者は、新しいレコード識別子を作成するときに、各レコードが属するレコード ゾーンを指定することを選択できます。 何も指定しない場合は、既定のレコード ゾーンが使用されます。
レコード識別子は、CKRecordID
クラスを介して CloudKit Framework で公開されます。 次のコードを使用して、新しいレコード識別子を作成できます:
var recordID = new CKRecordID("My Record");
リファレンス
リファレンスは、特定のデータベース内の関連するレコード間のリレーションシップを提供します:
上の例では、親が子を所有しているため、子は親レコードの子レコードです。 リレーションシップは、子レコードから親レコードに移動し、バック リファレンスと呼ばれます。
リファレンスは、CKReference
クラスを介して CloudKit Framework で公開されます。 これらは、iCloud サーバーにレコード間のリレーションシップを理解させる方法です。
リファレンスは、連鎖削除の背後にあるメカニズムを提供します。 親レコードがデータベースから削除されると、子レコード (リレーションシップで指定) もデータベースから自動的に削除されます。
Note
CloudKit を使用する場合、ダングリング ポインターが発生する可能性があります。 たとえば、アプリケーションがレコード ポインターのリストをフェッチし、レコードを選択してからレコードを要求するまでに、レコードがデータベースに存在しなくなった可能性があります。 この状況を適切に処理するには、アプリケーションをコーディングする必要があります。
必須ではありませんが、CloudKit Framework を使用する場合は、バック リファレンスを使用することをお勧めします。 Apple は、この最も効率的な種類のリファレンスになるようにシステムを微調整しています。
リファレンスを作成するときに、開発者は既にメモリ内にあるレコードを指定するか、レコード識別子へのリファレンスを作成できます。 レコード識別子を使用していて、指定したリファレンスがデータベースに存在しなかった場合は、ダングリング ポインターが作成されます。
既知のレコードに対するリファレンスを作成する例を次に示します:
var reference = new CKReference(newRecord, new CKReferenceAction());
アセット
資産では、大きな非構造化データのファイルを iCloud にアップロードし、特定のレコードに関連付けることができます:
クライアントでは、iCloud サーバーにアップロードするファイルを記述する CKRecord
が作成されます。 ファイルを含む CKAsset
が作成され、それを記述するレコードにリンクされます。
ファイルがサーバーにアップロードされると、レコードはデータベースに配置され、ファイルは特別な一括ストレージ データベースにコピーされます。 レコード ポインターとアップロードされたファイルの間にリンクが作成されます。
資産は、CKAsset
クラスを介して CloudKit Framework で公開され、大きな非構造化データを格納するために使用されます。 開発者はメモリ内に大きな非構造化データを含めたくないため、資産はディスク上のファイルを使用して実装されます。
資産はレコードによって所有されます。これにより、レコードをポインターとして使用して iCloud から資産を取得できます。 このようにして、資産を所有するレコードが削除されると、サーバーは資産をガベージ コレクションできます。
CKAssets
は大きなデータ ファイルを処理することを目的としているため、Apple は資産を効率的にアップロードしてダウンロードするように CloudKit を設計しました。
次のコードを使用して、資産を作成し、それをレコードに関連付けることができます:
var fileUrl = new NSUrl("LargeFile.mov");
var asset = new CKAsset(fileUrl);
newRecord ["name"] = asset;
これで、CloudKit 内のすべての基本的なオブジェクトについて説明しました。 コンテナーはアプリケーションに関連付けられ、データベースが含まれます。 データベースには、レコード ゾーンにグループ化され、レコード識別子によって指されるレコードが含まれます。 親と子のリレーションシップは、リファレンスを使用してレコード間で定義されます。 最後に、大きなファイルをアップロードし、資産を使用してレコードに関連付けることができます。
CloudKit Convenience API
Apple は、CloudKit を操作するための 2 つの異なる API セットを用意しています:
- Operational API – CloudKit のすべての機能を提供します。 より複雑なアプリケーションの場合、この API は CloudKit をきめ細かく制御します。
- Convenience API – CloudKit 機能の一般的な事前構成済みのサブセットを提供します。 iOS アプリケーションに CloudKit 機能を組み込むための便利で簡単なアクセス ソリューションを提供します。
Convenience API は通常、ほとんどの iOS アプリケーションに最適な選択であり、Apple はこれから使い始めることをすすめています。 このセクションの残りの部分では、次の便利な API トピックについて説明します:
- レコードの保存。
- レコードのフェッチ。
- レコードの更新。
一般的なセットアップ コード
CloudKit Convenience API の使用を開始する前に、いくつかの標準的なセットアップ コードが必要です。 まず、アプリケーションの AppDelegate.cs
ファイルを変更し、次のようにします:
using System;
using System.Collections.Generic;
using System.Linq;
using Foundation;
using UIKit;
using CloudKit;
namespace CloudKitAtlas
{
[Register ("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
public override UIWindow Window { get; set;}
public CKDatabase PublicDatabase { get; set; }
public CKDatabase PrivateDatabase { get; set; }
public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
{
application.RegisterForRemoteNotifications ();
// Get the default public and private databases for
// the application
PublicDatabase = CKContainer.DefaultContainer.PublicCloudDatabase;
PrivateDatabase = CKContainer.DefaultContainer.PrivateCloudDatabase;
return true;
}
public override void RegisteredForRemoteNotifications (UIApplication application, NSData deviceToken)
{
Console.WriteLine ("Registered for Push notifications with token: {0}", deviceToken);
}
public override void FailedToRegisterForRemoteNotifications (UIApplication application, NSError error)
{
Console.WriteLine ("Push subscription failed");
}
public override void ReceivedRemoteNotification (UIApplication application, NSDictionary userInfo)
{
Console.WriteLine ("Push received");
}
}
}
上記のコードでは、パブリックおよびプライベートの CloudKit データベースをショートカットとして公開し、アプリケーションの残りの部分で操作しやすくします。
次に、CloudKit を使用するビューまたはビュー コンテナーに次のコードを追加します:
using CloudKit;
//...
public AppDelegate ThisApp {
get { return (AppDelegate)UIApplication.SharedApplication.Delegate; }
}
これにより、 AppDelegate
にアクセスし、上記で作成したパブリック およびプライベート データベースのショートカットにアクセスするためのショートカットが追加されます。
このコードを用意して、Xamarin iOS 8 アプリケーションで CloudKit Convenience API を実装する方法を見てみましょう。
レコードの保存
レコードについて説明したときに示したパターンを使って、次のコードでは新しいレコードを作成し、Convenience API を使って公開データベースに保存します:
private const string ReferenceItemRecordName = "ReferenceItems";
...
// Create a new record
var newRecord = new CKRecord (ReferenceItemRecordName);
newRecord ["name"] = (NSString)nameTextField.Text;
// Save it to the database
ThisApp.PublicDatabase.SaveRecord(newRecord, (record, err) => {
// Was there an error?
if (err != null) {
...
}
});
上記のコードに関する 3 つの注意事項:
- 開発者は、
PublicDatabase
のSaveRecord
メソッドを呼び出してデータの送信方法、書き込み対象のゾーンなどを指定する必要はありません。Convenience API は、これらのすべての詳細自体を処理します。 - 呼び出しは非同期であり、成功または失敗のいずれかで呼び出しが完了したときにコールバック ルーチンを提供します。 呼び出しが失敗した場合は、エラー メッセージが表示されます。
- CloudKit では、ローカル ストレージ/永続化は提供されません。転送媒体のみです。 そのため、レコードを保存する要求が行われると、それはすぐに iCloud サーバーに送信されます。
Note
モバイル ネットワーク通信は"損失の多い" 性質があり、接続が常に切断されたり中断されたりするため、開発者が CloudKit で作業する際にまず考慮しなければならないことの 1 つはエラー処理です。
レコードのフェッチ。
レコードが作成され、iCloud サーバーに正常に格納されたら、次のコードを使用してレコードを取得します:
// Create a record ID and fetch the record from the database
var recordID = new CKRecordID("MyRecordName");
ThisApp.PublicDatabase.FetchRecord(recordID, (record, err) => {
// Was there an error?
if (err != null) {
...
}
});
レコードを保存する場合と同様に、上記のコードは非同期でシンプルであり、大きなエラー処理が必要です。
レコードを更新する
iCloud サーバーからレコードをフェッチした後、次のコードを使用してレコードを変更し、変更をデータベースに保存できます:
// Create a record ID and fetch the record from the database
var recordID = new CKRecordID("MyRecordName");
ThisApp.PublicDatabase.FetchRecord(recordID, (record, err) => {
// Was there an error?
if (err != null) {
} else {
// Modify the record
record["name"] = (NSString)"New Name";
// Save changes to database
ThisApp.PublicDatabase.SaveRecord(record, (r, e) => {
// Was there an error?
if (e != null) {
...
}
});
}
});
呼び出しが成功した場合、PublicDatabase
の FetchRecord
メソッドは CKRecord
を返します。 その後、アプリケーションはレコードを変更し、SaveRecord
をもう一度呼び出して、変更をデータベースに書き戻します。
このセクションでは、CloudKit Convenience API を使用するときにアプリケーションが使用する一般的なサイクルを示します。 アプリケーションは、iCloud にレコードを保存し、iCloud からそれらのレコードを取得し、レコードを変更して、それらの変更を iCloud に保存します。
スケーラビリティのための設計
ここまで、この記事では、iCloud サーバーで作業を行うたびに、アプリケーションのオブジェクト モデル全体を格納および取得する方法について説明しました。 この方法は、少量のデータと非常に小さなユーザー ベースで適切に機能しますが、情報やユーザー ベースの量が増えると、適切にスケーリングされません。
ビッグ データ、小さなデバイス
アプリケーションの人気が高いほど、データベース内のデータが多くなり、そのデータ全体のキャッシュをデバイスに保持する可能性が低くなります。 この問題を解決するには、次の手法を使用できます:
- 大きなデータをクラウドで維持 – CloudKit に保持すると、大きなデータを効率的に処理するように設計されています。
- クライアントは、そのデータのスライスのみを表示する必要があります – 特定の時点でタスクを処理するために必要な最小限のデータを停止します。
- クライアント ビューは変更できます - 各ユーザーの基本設定が異なるため、表示されるデータのスライスはユーザーごとに変更される可能性があり、特定のスライスのユーザーの個々のビューは異なる場合があります。
- クライアントでは、クエリを使用してビューポイント – クエリを使用すると、ユーザーはクラウド内に存在する大規模なデータセットの小さなサブセットを表示できます。
クエリ
前述のように、クエリを使用すると、開発者はクラウドに存在する大規模なデータセットの小さなサブセットを選択できます。 クエリは、CKQuery
クラスを介して CloudKit Framework で公開されます。
クエリは、レコードの種類 ( RecordType
)、述語 ( NSPredicate
)、および必要に応じて並べ替え記述子 ( NSSortDescriptors
) の 3 つの異なるものを結合します。 CloudKit では、ほとんどの NSPredicate
がサポートされています。
サポートされている述語
CloudKit では、クエリを使用する場合に、次の種類の NSPredicates
がサポートされます:
名前が変数に格納されている値と等しいレコードの照合:
NSPredicate.FromFormat(string.Format("name = '{0}'", recordName))
コンパイル時にキーを認識する必要がないように、動的キー値に基づく照合を許可します:
NSPredicate.FromFormat(string.Format("{0} = '{1}'", key, value))
レコードの値が指定された値より大きいレコードの照合:
NSPredicate.FromFormat(string.Format("start > {0}", (NSDate)date))
レコードの位置が特定の場所から 100 メートル以内にあるレコードの照合:
var location = new CLLocation(37.783,-122.404); var predicate = NSPredicate.FromFormat(string.Format("distanceToLocation:fromLocation(Location,{0}) < 100", location));
CloudKit では、トークン化された検索がサポートされます。 この呼び出しでは、2 つのトークンが作成されます。1 つは
after
用で、もう 1 つはsession
用です。 次の 2 つのトークンを含むレコードが返されます:NSPredicate.FromFormat(string.Format("ALL tokenize({0}, 'Cdl') IN allTokens", "after session"))
CloudKit では、
AND
演算子を使用して結合された複合述語がサポートされます。NSPredicate.FromFormat(string.Format("start > {0} AND name = '{1}'", (NSDate)date, recordName))
クエリの作成
次のコードを使用して、Xamarin iOS 8 アプリケーションで CKQuery
を作成できます:
var recordName = "MyRec";
var predicate = NSPredicate.FromFormat(string.Format("name = '{0}'", recordName));
var query = new CKQuery("CloudRecords", predicate);
最初に、特定の名前に一致するレコードのみを選択する述語を作成します。 次に、述語に一致する特定のレコードの種類のレコードを選択するクエリを作成します。
クエリの実行
クエリが作成されたら、次のコードを使用してクエリを実行し、返されたレコードを処理します:
var recordName = "MyRec";
var predicate = NSPredicate.FromFormat(string.Format("name = {0}", recordName));
var query = new CKQuery("CloudRecords", predicate);
ThisApp.PublicDatabase.PerformQuery(query, CKRecordZone.DefaultRecordZone().ZoneId, (NSArray results, NSError err) => {
// Was there an error?
if (err != null) {
...
} else {
// Process the returned records
for(nint i = 0; i < results.Count; ++i) {
var record = (CKRecord)results[i];
}
}
});
上記のコードは、上記で作成したクエリを受け取り、パブリック データベースに対して実行します。 レコード ゾーンが指定されていないため、すべてのゾーンが検索されます。 エラーが発生しなかった場合は、クエリのパラメーターと一致する CKRecords
の配列が返されます。
クエリについて考える方法は、クエリがポーリングであり、大規模なデータセットをスライスするのに優れているということです。 ただし、クエリは、次の理由により、大規模でほとんど静的なデータセットには適していません:
- これらは、デバイスのバッテリ寿命によくありません。
- これらはネットワーク トラフィックに適していません。
- 表示される情報は、アプリケーションがデータベースをポーリングする頻度によって制限されるため、ユーザー エクスペリエンスにはよくありません。 ユーザーは現在、何かが変更されたときにプッシュ通知を受け取ります。
サブスクリプション
大部分が静的な大規模なデータセットを処理する場合は、クライアント デバイスでクエリを実行しないでください。クエリは、クライアントの代わりにサーバー上で実行する必要があります。 クエリはバックグラウンドで実行する必要があり、現在のデバイスまたは同じデータベースにアクセスしている別のデバイスによって、1 つのレコードを保存するたびに実行する必要があります。
最後に、サーバー側クエリの実行時に、データベースに接続されているすべてのデバイスにプッシュ通知を送信する必要があります。
サブスクリプションは、CKSubscription
クラスを介して CloudKit Framework で公開されます。 レコードの種類 ( RecordType
)、述語 ( NSPredicate
) と Apple プッシュ通知 ( Push
) を組み合わせます。
Note
CloudKit プッシュには、プッシュの原因などの CloudKit 固有の情報を含むペイロードが含まれるため、少し拡張されます。
サブスクリプションのしくみ
C# コードでサブスクリプションを実装する前に、サブスクリプションのしくみの概要を簡単に見てみましょう:
上のグラフは、一般的なサブスクリプション プロセスを次のように示しています:
- クライアント デバイスは、サブスクリプションをトリガーする一連の条件と、トリガーが発生したときに送信されるプッシュ通知を含む新しいサブスクリプションを作成します。
- サブスクリプションは、既存のサブスクリプションのコレクションに追加されるデータベースに送信されます。
- 2 つ目のデバイスは、新しいレコードを作成し、そのレコードをデータベースに保存します。
- データベースは、サブスクリプションの一覧を検索して、新しいレコードがいずれかの条件に一致するかどうかを確認します。
- 一致するものが見つかった場合、プッシュ通知は、トリガーの原因となったレコードに関する情報を使用してサブスクリプションを登録したデバイスに送信されます。
この知識を活かしながら、Xamarin iOS 8 アプリケーションでサブスクリプションを作成する方法を見てみましょう。
サブスクリプションの作成
次のコードを使用してサブスクリプションを作成できます:
// Create a new subscription
DateTime date;
var predicate = NSPredicate.FromFormat(string.Format("start > {0}", (NSDate)date));
var subscription = new CKSubscription("RecordType", predicate, CKSubscriptionOptions.FiresOnRecordCreation);
// Describe the type of notification
var notificationInfo = new CKNotificationInfo();
notificationInfo.AlertLocalizationKey = "LOCAL_NOTIFICATION_KEY";
notificationInfo.SoundName = "ping.aiff";
notificationInfo.ShouldBadge = true;
// Attach the notification info to the subscription
subscription.NotificationInfo = notificationInfo;
最初に、サブスクリプションをトリガーする条件を提供する述語を作成します。 次に、特定のレコードの種類に対してサブスクリプションを作成し、トリガーのテスト時のオプションを設定します。 最後に、サブスクリプションがトリガーされたときに発生する通知の種類を定義し、サブスクリプションをアタッチします。
サブスクリプションの保存
サブスクリプションが作成されると、次のコードによってデータベースに保存されます:
// Save the subscription to the database
ThisApp.PublicDatabase.SaveSubscription(subscription, (s, err) => {
// Was there an error?
if (err != null) {
}
});
Convenience API を使用すると、呼び出しは非同期でシンプルで、簡単なエラー処理を提供します。
プッシュ通知の処理
開発者が以前に Apple Push Notifications (APNs) を使用している場合は、CloudKit によって生成された通知を処理するプロセスをよく理解している必要があります。
AppDelegate.cs
で、ReceivedRemoteNotification
クラスを次のようにオーバーライドします:
public override void ReceivedRemoteNotification (UIApplication application, NSDictionary userInfo)
{
// Parse the notification into a CloudKit Notification
var notification = CKNotification.FromRemoteNotificationDictionary (userInfo);
// Get the body of the message
var alertBody = notification.AlertBody;
// Was this a query?
if (notification.NotificationType == CKNotificationType.Query) {
// Yes, convert to a query notification and get the record ID
var query = notification as CKQueryNotification;
var recordID = query.RecordId;
}
}
上記のコードは、UserInfo を CloudKit 通知に解析するように CloudKit に求めます。 次に、アラートに関する情報が抽出されます。 最後に、通知の種類がテストされ、それに応じて通知が処理されます。
このセクションでは、クエリとサブスクリプションを使用して、上記で示したビッグ データの小さなデバイスの問題に回答する方法について説明しました。 アプリケーションは、その大きなデータをクラウドに残し、これらのテクノロジを使用してこのデータセットにビューを提供します。
CloudKit ユーザー アカウント
この記事の冒頭で説明したように、CloudKit は既存の iCloud インフラストラクチャの上に構築されています。 次のセクションでは、CloudKit API を使用してアカウントを開発者に公開する方法について詳しく説明します。
認証
ユーザー アカウントを処理する場合、最初の考慮事項は認証です。 CloudKit では、デバイスで現在ログインしている iCloud ユーザーによる認証がサポートされています。 認証はバックグラウンドで行われ、iOS によって処理されます。 このように、開発者は認証の実装の詳細について心配する必要はありません。 ユーザーがログオンしているかどうかをテストするだけです。
ユーザー アカウント情報
CloudKit は、開発者に次のユーザー情報を提供します:
- ID – ユーザーを一意に識別する方法です。
- メタデータ – ユーザーに関する情報を保存および取得する機能。
- プライバシー – すべての情報はプライバシーに配慮したマナーで処理されます。 ユーザーが同意しない限り、何も公開されません。
- 検出 – ユーザーは、同じアプリケーションを使用しているフレンドを検出できます。
次に、これらのトピックについて詳しく説明します。
ID
上述のように、CloudKit は、アプリケーションが特定のユーザーを一意に識別する方法を提供します:
ユーザーのデバイス上で実行されているクライアント アプリケーションと、CloudKit コンテナー内のすべての特定のユーザー プライベート データベースがあります。 クライアント アプリケーションは、これらの特定のユーザーのいずれかにリンクされます。 これは、デバイス上でローカルに iCloud にログインしているユーザーに基づいています。
これは iCloud から来ているので、ユーザー情報の豊富なバッキング ストアがあります。 また、iCloud は実際にコンテナーをホストしているため、ユーザーを関連付けることができます。 上の図では、iCloud アカウントが user@icloud.com
であるユーザーが現在のクライアントにリンクされています。
コンテナーごとに、ランダムに生成された一意のユーザー ID が作成され、ユーザーの iCloud アカウント (メール アドレス) に関連付けられます。 このユーザー ID はアプリケーションに返され、開発者が適切と見なす任意の方法で使用できます。
Note
同じ iCloud ユーザーに対して同じデバイスで実行されている異なるアプリケーションは、異なる CloudKit コンテナーに接続されているため、ユーザー ID が異なります。
次のコードは、デバイスで現在ログインしている iCloud ユーザーの CloudKit ユーザー ID を取得します:
public CKRecordID UserID { get; set; }
...
// Get the CloudKit User ID
CKContainer.DefaultContainer.FetchUserRecordId ((recordID, err) => {
// Was there an error?
if (err!=null) {
Console.WriteLine("Error: {0}", err.LocalizedDescription);
} else {
// Save user ID
UserID = recordID;
}
});
上記のコードでは、現在ログインしているユーザーの ID を提供するように CloudKit コンテナーに要求しています。 この情報は iCloud サーバーから取得されるため、呼び出しは非同期であり、エラー処理が必要です。
Metadata
CloudKit の各ユーザーには、それらを記述する特定のメタデータがあります。 このメタデータは、CloudKit レコードとして表されます:
コンテナーの特定のユーザーのプライベート データベース内を見ると、そのユーザーを定義するレコードが 1 つあります。 パブリック データベース内には、コンテナーのユーザーごとに 1 つずつ、多数のユーザー レコードがあります。 そのうちの 1 つは、現在ログオンしているユーザーのレコード ID と一致するレコード ID を持つことになります。
パブリック データベースのユーザー レコードは、ユーザー全員が読み取り可能です。 これらは、ほとんどの場合、通常のレコードとして扱われ、CKRecordTypeUserRecord
の種類を持っています。 これらのレコードはシステムによって予約されており、クエリでは使用できません。
ユーザー レコードにアクセスするには、次のコードを使用します:
public CKRecord UserRecord { get; set; }
...
// Get the user's record
PublicDatabase.FetchRecord(UserID, (record ,er) => {
//was there an error?
if (er != null) {
Console.WriteLine("Error: {0}", er.LocalizedDescription);
} else {
// Save the user record
UserRecord = record;
}
});
上記のコードでは、パブリック データベースに対して、上記でアクセスした ID のユーザー レコードを返すように求めます。 この情報は iCloud サーバーから取得されるため、呼び出しは非同期であり、エラー処理が必要です。
プライバシー
CloudKit は、現在ログオンしているユーザーのプライバシーを保護するために、既定で設計されていました。 既定では、ユーザーに関する個人を識別する情報は公開されません。 アプリケーションでユーザーに関する限られた情報が必要になる場合があります。
このような場合、アプリケーションはユーザーにこの情報の開示を要求できます。 自分のアカウント情報の公開をオプトインするようユーザーに求めるダイアログ ボックスが表示されます。
探索
ユーザーがユーザー アカウント情報へのアクセスをアプリケーションに制限することをオプトインしていると仮定すると、ユーザーはアプリケーションの他のユーザーが検出できます:
クライアント アプリケーションがコンテナーと通信しており、コンテナーが iCloud と通信してユーザー情報にアクセスしています。 ユーザーはメール アドレスを指定でき、探索を使用してユーザーに関する情報を取得できます。 必要に応じて、ユーザー ID を使用してユーザーに関する情報を検出することもできます。
CloudKit では、アドレス帳全体に対してクエリを実行して、現在ログオンしている iCloud ユーザーの友達である可能性があるユーザーに関する情報を検出する方法も提供されます。 CloudKit プロセスは、ユーザーの連絡先ブックを取得し、メール アドレスを使用して、それらのアドレスに一致する他のユーザーのアプリケーションを見つけることができるかどうかを確認します。
これにより、アプリケーションは、アクセス権を提供したり、ユーザーに連絡先へのアクセスの承認を求めたりすることなく、ユーザーの連絡先ブックを活用できます。 アプリケーションで使用できる連絡先情報は、CloudKit プロセスのみがアクセスできます。
要約すると、ユーザー検出には次の 3 種類の入力を使用できます:
- ユーザー レコード ID – 検出は、現在ログインしている CloudKit ユーザーのユーザー ID に対して実行できます。
- ユーザーのメール アドレス – ユーザーはメール アドレスを指定でき、検出に使用できます。
- 連絡先ブック – ユーザーのアドレス帳を使用して、連絡先に記載されているものと同じメール アドレスを持つアプリケーションのユーザーを検出できます。
ユーザー検出では、次の情報が返されます:
- ユーザー レコード ID - パブリック データベース内のユーザーの一意の ID。
- 名と姓 - パブリック データベースに格納されます。
この情報は、検索にオプトインしたユーザーに対してのみ返されます。
次のコードは、デバイスで現在 iCloud にログインしているユーザーに関する情報を検出します:
public CKDiscoveredUserInfo UserInfo { get; set; }
//...
// Get the user's metadata
CKContainer.DefaultContainer.DiscoverUserInfo(UserID, (info, e) => {
// Was there an error?
if (e != null) {
Console.WriteLine("Error: {0}", e.LocalizedDescription);
} else {
// Save the user info
UserInfo = info;
}
});
次のコードを使用して、Contact Book のすべてのユーザーにクエリを実行します:
// Ask CloudKit for all of the user's friends information
CKContainer.DefaultContainer.DiscoverAllContactUserInfos((info, er) => {
// Was there an error
if (er != null) {
Console.WriteLine("Error: {0}", er.LocalizedDescription);
} else {
// Process all returned records
for(int i = 0; i < info.Count(); ++i) {
// Grab a user
var userInfo = info[i];
}
}
});
このセクションでは、CloudKit がアプリケーションに提供できるユーザーのアカウントへのアクセスの 4 つの主要な領域について説明しました。 ユーザーの ID とメタデータの取得から、CloudKit に組み込まれているプライバシー ポリシー、最後にアプリケーションの他のユーザーを検出する機能まで。
開発環境と運用環境
CloudKit は、アプリケーションのレコードの種類とデータに個別の開発環境と運用環境を提供します。 開発環境は、開発チームのメンバーのみが使用できる、より柔軟な環境です。 アプリケーションが新しいフィールドをレコードに追加し、そのレコードを開発環境に保存すると、サーバーはスキーマ情報を自動的に更新します。
開発者はこの機能を使用して、開発中にスキーマを変更できるため、時間を節約できます。 1 つの注意事項は、フィールドがレコードに追加された後、そのフィールドに関連付けられているデータ型をプログラムで変更できないことです。 フィールドの種類を変更するには、開発者は CloudKit ダッシュボードのフィールドを削除し、新しい型で再度追加する必要があります。
アプリケーションをデプロイする前に、開発者は CloudKit ダッシュボードを使用して、スキーマとデータ運用環境に移行できます。 運用環境に対して実行すると、サーバーはアプリケーションがプログラムによってスキーマを変更できないようにします。 開発者は引き続き CloudKit ダッシュボードを使って変更を加えることができますが、運用環境のレコードにフィールドを追加しようとするとエラーが発生します。
Note
iOS シミュレーターは、開発環境でのみ動作します。 開発者が運用環境でアプリケーションをテストする準備ができたら、物理 iOS デバイスが必要です。
CloudKit 対応アプリの発送
CloudKit を使用するアプリケーションを出荷する前に、運用 CloudKit 環境をターゲットにするように構成する必要があります。そうでない場合、アプリケーションは Apple によって拒否されます。
次の操作を行います。
Visual Studio for Mac で、[リリース]>[iOS デバイス] 用のアプリケーションをコンパイルします:
[ビルド] メニューの [アーカイブ] を選択します:
アーカイブが作成され、Visual Studio for Mac に表示されます:
Xcode を起動します。
[ウィンドウ] メニューから、[オーガナイザー] を選択します:
アプリケーションのアーカイブを選択し、[エクスポート...] ボタンをクリックします:
エクスポートする方法を選択し、[次へ] ボタン クリックします:
ドロップダウン リストから [開発チーム] を選択し、[選択] ボタンをクリックします:
ドロップダウン リストから [運用] を選択し、[次へ] ボタンをクリックします:
設定を確認し、[エクスポート] ボタンをクリックします:
結果のアプリケーション
.ipa
ファイルを生成する場所を選択します。
このプロセスは、アプリケーションを iTunes Connect に直接送信する場合と似ていますが、オーガナイザー ウィンドウでアーカイブを選択した後、[エクスポート...] の代わりに [送信...] ボタンをクリックするだけです。
CloudKit を使用するタイミング
この記事で説明したように、CloudKit は、アプリケーションが iCloud サーバーから情報を格納および取得するための簡単な方法を提供します。 つまり、CloudKit は既存のツールやフレームワークを廃止したり非推奨にしたりすることはありません。
ユース ケース
次のユース ケースは、開発者が特定の iCloud フレームワークまたはテクノロジを使用するタイミングを決定するのに役立ちます:
- iCloud のキーと値のストア – は、少量のデータを非同期的に最新の状態に保ち、アプリケーションの環境設定を操作するのに最適です。 ただし、情報の量が非常に少ない場合は制約されます。
- iCloud Drive – 既存の iCloud ドキュメント API 上に構築され、ファイル システムから非構造化データを同期するための簡単な API を提供します。 これは Mac OS X で完全なオフライン キャッシュを提供し、ドキュメント中心のアプリケーションに最適です。
- iCloud Core Data – ユーザーのすべてのデバイス間でデータをレプリケートできるようにします。 このデータはシングル ユーザーであり、プライベートで構造化されたデータの同期を維持するのに最適です。
- CloudKit – 構造体と一括の両方のパブリック データを提供し、大規模なデータセットと大規模な非構造化ファイルの両方を処理できます。 これはユーザーの iCloud アカウントに関連付けられ、クライアントによるデータ転送を提供します。
これらのユース ケースを念頭に置いて、開発者は、現在必要なアプリケーション機能の両方を提供し、将来の成長に適したスケーラビリティを提供するために、適切な iCloud テクノロジを選択する必要があります。
まとめ
この記事では、CloudKit API の簡単な概要について説明しました。 ここでは、CloudKit を使用するように Xamarin iOS アプリケーションをプロビジョニングおよび構成する方法について説明しました。 CloudKit Convenience API の機能について説明しました。 クエリとサブスクリプションを使用して、スケーラビリティのために CloudKit 対応アプリケーションを設計する方法について説明しました。 最後に、CloudKit によってアプリケーションに公開されているユーザー アカウント情報が表示されます。