Menggunakan iCloud dengan Xamarin.iOS

API penyimpanan iCloud di iOS 5 memungkinkan aplikasi menyimpan dokumen pengguna dan data khusus aplikasi ke lokasi pusat dan mengakses item tersebut dari semua perangkat pengguna.

Ada empat jenis penyimpanan yang tersedia:

  • Penyimpanan Kunci-Nilai - untuk berbagi sejumlah kecil data dengan aplikasi Anda di perangkat lain pengguna.

  • Penyimpanan UIDocument - untuk menyimpan dokumen dan data lain di akun iCloud pengguna menggunakan subkelas UIDocument.

  • CoreData - Penyimpanan database SQLite.

  • File dan direktori individual - untuk mengelola banyak file yang berbeda langsung dalam sistem file.

Dokumen ini membahas dua jenis pertama - Pasangan Kunci-Nilai dan subkelas UIDocument - dan cara menggunakan fitur tersebut di Xamarin.iOS.

Penting

Apple menyediakan alat untuk membantu pengembang menangani Peraturan Perlindungan Data Umum (GDPR) Uni Eropa dengan benar.

Persyaratan

  • Versi stabil terbaru Xamarin.iOS
  • Xcode 10
  • Visual Studio untuk Mac atau Visual Studio 2019.

Mempersiapkan pengembangan iCloud

Aplikasi harus dikonfigurasi untuk menggunakan iCloud baik di Portal Provisi Apple maupun proyek itu sendiri. Sebelum mengembangkan untuk iCloud (atau mencoba sampel) ikuti langkah-langkah di bawah ini.

Untuk mengonfigurasi aplikasi dengan benar untuk mengakses iCloud:

  • Temukan TeamID Anda - masuk ke developer.apple.com dan kunjungi Pusat > Anggota Ringkasan Akun Pengembang Akun > Anda untuk mendapatkan ID Tim Anda (atau ID Individu untuk pengembang tunggal). Ini akan menjadi string 10 karakter ( A93A5CM278 misalnya) - bentuk ini merupakan bagian dari "pengidentifikasi kontainer".

  • Buat ID Aplikasi baru - Untuk membuat ID Aplikasi, ikuti langkah-langkah yang diuraikan di bagian Provisi untuk Teknologi Toko dari panduan Provisi Perangkat, dan pastikan untuk memeriksa iCloud sebagai layanan yang diizinkan:

Check iCloud as an allowed service

  • Buat Profil Provisi baru - Untuk membuat Profil Provisi, ikuti langkah-langkah yang diuraikan dalam panduan Provisi Perangkat .

  • Tambahkan Pengidentifikasi Kontainer ke Entitlements.plist - format pengidentifikasi kontainer adalah TeamID.BundleID. Untuk informasi selengkapnya, lihat panduan Bekerja dengan Pemberian Izin.

  • Konfigurasikan properti proyek - Dalam file Info.plist pastikan Pengidentifikasi Bundel cocok dengan ID Bundel yang ditetapkan saat membuat ID Aplikasi; Penandatanganan Bundel iOS menggunakan Profil Provisi yang berisi ID Aplikasi dengan iCloud App Service, dan file Pemberian Izin Kustom dipilih. Ini semua dapat dilakukan di Visual Studio di bawah panel Properti proyek.

  • Aktifkan iCloud di perangkat Anda - buka Pengaturan > iCloud dan pastikan perangkat masuk. Pilih dan aktifkan opsi Dokumen & Data .

  • Anda harus menggunakan perangkat untuk menguji iCloud - perangkat tidak akan berfungsi di Simulator. Bahkan, Anda benar-benar memerlukan dua perangkat atau lebih yang semuanya masuk dengan ID Apple yang sama untuk melihat iCloud beraksi.

Penyimpanan Kunci-Nilai

Penyimpanan kunci-nilai ditujukan untuk sejumlah kecil data yang mungkin disukai pengguna di seluruh perangkat - seperti halaman terakhir yang mereka lihat dalam buku atau majalah. Penyimpanan kunci-nilai tidak boleh digunakan untuk mencadangkan data.

Ada beberapa batasan yang perlu diperhatikan saat menggunakan penyimpanan kunci-nilai:

  • Ukuran kunci maksimum - Nama kunci tidak boleh lebih panjang dari 64 byte.

  • Ukuran nilai maksimum - Anda tidak dapat menyimpan lebih dari 64 kilobyte dalam satu nilai.

  • Ukuran penyimpanan kunci-nilai maksimum untuk aplikasi - Aplikasi hanya dapat menyimpan hingga 64 kilobyte data kunci-nilai secara total. Upaya untuk mengatur kunci di luar batas tersebut akan gagal dan nilai sebelumnya akan bertahan.

  • Jenis data - Hanya jenis dasar seperti string, angka, dan boolean yang dapat disimpan.

Contoh iCloudKeyValue menunjukkan cara kerjanya. Kode sampel membuat kunci bernama untuk setiap perangkat: Anda dapat mengatur kunci ini pada satu perangkat dan melihat nilai disebarkan ke perangkat lain. Ini juga membuat kunci yang disebut "Bersama" yang dapat diedit di perangkat apa pun - jika Anda mengedit di banyak perangkat sekaligus, iCloud akan memutuskan nilai mana yang "menang" (menggunakan tanda waktu pada perubahan) dan disebarluaskan.

Cuplikan layar ini memperlihatkan sampel yang digunakan. Saat pemberitahuan perubahan diterima dari iCloud, pemberitahuan tersebut dicetak dalam tampilan teks gulir di bagian bawah layar dan diperbarui di bidang input.

The flow of messages between devices

Mengatur dan mengambil data

Kode ini menunjukkan cara mengatur nilai string.

var store = NSUbiquitousKeyValueStore.DefaultStore;
store.SetString("testkey", "VALUE IN THE CLOUD");  // key and value
store.Synchronize();

Memanggil Synchronize memastikan nilai hanya dipertahankan ke penyimpanan disk lokal. Sinkronisasi ke iCloud terjadi di latar belakang dan tidak dapat "dipaksakan" oleh kode aplikasi. Dengan konektivitas jaringan yang baik, sinkronisasi akan sering terjadi dalam waktu 5 detik, namun jika jaringan buruk (atau terputus) pembaruan mungkin memakan waktu lebih lama.

Anda dapat mengambil nilai dengan kode ini:

var store = NSUbiquitousKeyValueStore.DefaultStore;
display.Text = store.GetString("testkey");

Nilai diambil dari penyimpanan data lokal - metode ini tidak mencoba menghubungi server iCloud untuk mendapatkan nilai "terbaru". iCloud akan memperbarui penyimpanan data lokal sesuai dengan jadwalnya sendiri.

Menghapus data

Untuk menghapus pasangan kunci-nilai sepenuhnya, gunakan metode Hapus seperti ini:

var store = NSUbiquitousKeyValueStore.DefaultStore;
store.Remove("testkey");
store.Synchronize();

Mengamati Perubahan

Aplikasi juga dapat menerima pemberitahuan saat nilai diubah oleh iCloud dengan menambahkan pengamat ke NSNotificationCenter.DefaultCenter. Kode berikut dari metode KeyValueViewController.csViewWillAppear menunjukkan cara mendengarkan pemberitahuan tersebut dan membuat daftar kunci mana yang telah diubah:

keyValueNotification =
NSNotificationCenter.DefaultCenter.AddObserver (
    NSUbiquitousKeyValueStore.DidChangeExternallyNotification, notification => {
    Console.WriteLine ("Cloud notification received");
    NSDictionary userInfo = notification.UserInfo;

    var reasonNumber = (NSNumber)userInfo.ObjectForKey (NSUbiquitousKeyValueStore.ChangeReasonKey);
    nint reason = reasonNumber.NIntValue;

    var changedKeys = (NSArray)userInfo.ObjectForKey (NSUbiquitousKeyValueStore.ChangedKeysKey);
    var changedKeysList = new List<string> ();
    for (uint i = 0; i < changedKeys.Count; i++) {
        var key = changedKeys.GetItem<NSString> (i); // resolve key to a string
        changedKeysList.Add (key);
    }
    // now do something with the list...
});

Kode Anda kemudian dapat mengambil beberapa tindakan dengan daftar kunci yang diubah, seperti memperbarui salinan lokalnya atau memperbarui UI dengan nilai baru.

Kemungkinan alasan perubahan adalah: ServerChange (0), InitialSyncChange (1), atau QuotaViolationChange (2). Anda dapat mengakses alasan dan melakukan pemrosesan yang berbeda jika diperlukan (misalnya, Anda mungkin perlu menghapus beberapa kunci sebagai akibat dari QuotaViolationChange).

Penyimpanan Dokumen

Penyimpanan Dokumen iCloud dirancang untuk mengelola data yang penting untuk aplikasi Anda (dan kepada pengguna). Ini dapat digunakan untuk mengelola file dan data lain yang perlu dijalankan aplikasi Anda, sementara pada saat yang sama menyediakan fungsionalitas pencadangan dan berbagi berbasis iCloud di semua perangkat pengguna.

Diagram ini menunjukkan bagaimana semuanya cocok bersama-sama. Setiap perangkat memiliki data yang disimpan di penyimpanan lokal (UbiquityContainer) dan iCloud Daemon sistem operasi mengurus pengiriman dan penerimaan data di cloud. Semua akses file ke UbiquityContainer harus dilakukan melalui FilePresenter/FileCoordinator untuk mencegah akses bersamaan. Kelas UIDocument mengimplementasikannya untuk Anda; contoh ini menunjukkan cara menggunakan UIDocument.

The document storage overview

Contoh iCloudUIDoc mengimplementasikan subkelas sederhana UIDocument yang berisi satu bidang teks. Teks dirender dalam dan UITextView pengeditan disebarkan oleh iCloud ke perangkat lain dengan pesan pemberitahuan yang ditampilkan dengan warna merah. Kode sampel tidak berurusan dengan fitur iCloud yang lebih canggih seperti resolusi konflik.

Cuplikan layar ini menunjukkan aplikasi sampel - setelah mengubah teks dan menekan UpdateChangeCount , dokumen disinkronkan melalui iCloud ke perangkat lain.

This screenshot shows the sample application after changing the text and pressing UpdateChangeCount

Ada lima bagian untuk sampel iCloudUIDoc:

  1. Mengakses UbiquityContainer - tentukan apakah iCloud diaktifkan, dan jika demikian jalur ke area penyimpanan iCloud aplikasi Anda.

  2. Membuat subkelas UIDocument - buat kelas untuk menengah antara penyimpanan iCloud dan objek model Anda.

  3. Menemukan dan membuka dokumen iCloud - gunakan NSFileManager dan NSPredicate untuk menemukan dokumen iCloud dan membukanya.

  4. Menampilkan dokumen iCloud - mengekspos properti dari Anda UIDocument sehingga Anda dapat berinteraksi dengan kontrol UI.

  5. Menyimpan dokumen iCloud - pastikan bahwa perubahan yang dilakukan di UI dipertahankan ke disk dan iCloud.

Semua operasi iCloud berjalan (atau harus berjalan) secara asinkron sehingga tidak memblokir saat menunggu sesuatu terjadi. Anda akan melihat tiga cara berbeda untuk mencapai ini dalam sampel:

Utas - dalam AppDelegate.FinishedLaunching panggilan awal dilakukan GetUrlForUbiquityContainer pada utas lain untuk mencegah pemblokiran utas utama.

NotificationCenter - mendaftar untuk pemberitahuan ketika operasi asinkron seperti NSMetadataQuery.StartQuery selesai.

Handler Penyelesaian - meneruskan metode untuk berjalan pada penyelesaian operasi asinkron seperti UIDocument.Open.

Mengakses UbiquityContainer

Langkah pertama dalam menggunakan Penyimpanan Dokumen iCloud adalah menentukan apakah iCloud diaktifkan, dan jika demikian lokasi "kontainer ubiquity" (direktori tempat file yang diaktifkan iCloud disimpan di perangkat).

Kode ini dalam AppDelegate.FinishedLaunching metode sampel.

// GetUrlForUbiquityContainer is blocking, Apple recommends background thread or your UI will freeze
ThreadPool.QueueUserWorkItem (_ => {
    CheckingForiCloud = true;
    Console.WriteLine ("Checking for iCloud");
    var uburl = NSFileManager.DefaultManager.GetUrlForUbiquityContainer (null);
    // OR instead of null you can specify "TEAMID.com.your-company.ApplicationName"

    if (uburl == null) {
        HasiCloud = false;
        Console.WriteLine ("Can't find iCloud container, check your provisioning profile and entitlements");

        InvokeOnMainThread (() => {
            var alertController = UIAlertController.Create ("No \uE049 available",
            "Check your Entitlements.plist, BundleId, TeamId and Provisioning Profile!", UIAlertControllerStyle.Alert);
            alertController.AddAction (UIAlertAction.Create ("OK", UIAlertActionStyle.Destructive, null));
            viewController.PresentViewController (alertController, false, null);
        });
    } else { // iCloud enabled, store the NSURL for later use
        HasiCloud = true;
        iCloudUrl = uburl;
        Console.WriteLine ("yyy Yes iCloud! {0}", uburl.AbsoluteUrl);
    }
    CheckingForiCloud = false;
});

Meskipun sampel tidak melakukannya, Apple merekomendasikan untuk memanggil GetUrlForUbiquityContainer setiap kali aplikasi datang ke latar depan.

Membuat Subkelas UIDocument

Semua file dan direktori iCloud (yaitu apa pun yang disimpan di direktori UbiquityContainer) harus dikelola menggunakan metode NSFileManager, menerapkan protokol NSFilePresenter dan menulis melalui NSFileCoordinator. Cara paling sederhana untuk melakukan semua itu adalah dengan tidak menulisnya sendiri, tetapi subkelas UIDocument yang melakukan semuanya untuk Anda.

Hanya ada dua metode yang harus Anda terapkan dalam subkelas UIDocument untuk bekerja dengan iCloud:

  • LoadFromContents - meneruskan NSData konten file untuk Anda bongkar ke kelas/es model Anda.

  • ContentsForType - meminta Anda untuk menyediakan representasi NSData dari kelas/es model Anda untuk disimpan ke disk (dan Cloud).

Kode sampel dari iCloudUIDoc\MonkeyDocument.cs ini menunjukkan cara menerapkan UIDocument.

public class MonkeyDocument : UIDocument
{
    // the 'model', just a chunk of text in this case; must easily convert to NSData
    NSString dataModel;
    // model is wrapped in a nice .NET-friendly property
    public string DocumentString {
        get {
            return dataModel.ToString ();
        }
        set {
            dataModel = new NSString (value);
        }
    }
    public MonkeyDocument (NSUrl url) : base (url)
    {
        DocumentString = "(default text)";
    }
    // contents supplied by iCloud to display, update local model and display (via notification)
    public override bool LoadFromContents (NSObject contents, string typeName, out NSError outError)
    {
        outError = null;

        Console.WriteLine ("LoadFromContents({0})", typeName);

        if (contents != null)
            dataModel = NSString.FromData ((NSData)contents, NSStringEncoding.UTF8);

        // LoadFromContents called when an update occurs
        NSNotificationCenter.DefaultCenter.PostNotificationName ("monkeyDocumentModified", this);
        return true;
    }
    // return contents for iCloud to save (from the local model)
    public override NSObject ContentsForType (string typeName, out NSError outError)
    {
        outError = null;

        Console.WriteLine ("ContentsForType({0})", typeName);
        Console.WriteLine ("DocumentText:{0}",dataModel);

        NSData docData = dataModel.Encode (NSStringEncoding.UTF8);
        return docData;
    }
}

Model data dalam hal ini sangat sederhana - bidang teks tunggal. Model data Anda bisa serumit yang diperlukan, seperti dokumen Xml atau data biner. Peran utama implementasi UIDocument adalah menerjemahkan antara kelas model Anda dan representasi NSData yang dapat disimpan/dimuat pada disk.

Menemukan dan Membuka Dokumen iCloud

Aplikasi sampel hanya menangani satu file - test.txt - sehingga kode di AppDelegate.cs membuat NSPredicate dan NSMetadataQuery untuk mencari nama file tersebut secara khusus. Berjalan NSMetadataQuery secara asinkron dan mengirim pemberitahuan saat selesai. DidFinishGathering dipanggil oleh pengamat pemberitahuan, menghentikan kueri dan memanggil LoadDocument, yang menggunakan UIDocument.Open metode dengan handler penyelesaian untuk mencoba memuat file dan menampilkannya dalam MonkeyDocumentViewController.

string monkeyDocFilename = "test.txt";
void FindDocument ()
{
    Console.WriteLine ("FindDocument");
    query = new NSMetadataQuery {
        SearchScopes = new NSObject [] { NSMetadataQuery.UbiquitousDocumentsScope }
    };

    var pred = NSPredicate.FromFormat ("%K == %@", new NSObject[] {
        NSMetadataQuery.ItemFSNameKey, new NSString (MonkeyDocFilename)
    });

    Console.WriteLine ("Predicate:{0}", pred.PredicateFormat);
    query.Predicate = pred;

    NSNotificationCenter.DefaultCenter.AddObserver (
        this,
        new Selector ("queryDidFinishGathering:"),
        NSMetadataQuery.DidFinishGatheringNotification,
        query
    );

    query.StartQuery ();
}

[Export ("queryDidFinishGathering:")]
void DidFinishGathering (NSNotification notification)
{
    Console.WriteLine ("DidFinishGathering");
    var metadataQuery = (NSMetadataQuery)notification.Object;
    metadataQuery.DisableUpdates ();
    metadataQuery.StopQuery ();

    NSNotificationCenter.DefaultCenter.RemoveObserver (this, NSMetadataQuery.DidFinishGatheringNotification, metadataQuery);
    LoadDocument (metadataQuery);
}

void LoadDocument (NSMetadataQuery metadataQuery)
{
    Console.WriteLine ("LoadDocument");

    if (metadataQuery.ResultCount == 1) {
        var item = (NSMetadataItem)metadataQuery.ResultAtIndex (0);
        var url = (NSUrl)item.ValueForAttribute (NSMetadataQuery.ItemURLKey);
        doc = new MonkeyDocument (url);

        doc.Open (success => {
            if (success) {
                Console.WriteLine ("iCloud document opened");
                Console.WriteLine (" -- {0}", doc.DocumentString);
                viewController.DisplayDocument (doc);
            } else {
                Console.WriteLine ("failed to open iCloud document");
            }
        });
    } // TODO: if no document, we need to create one
}

Menampilkan Dokumen iCloud

Menampilkan UIDocument tidak boleh berbeda dengan kelas model lain - properti ditampilkan dalam kontrol UI, mungkin diedit oleh pengguna dan kemudian ditulis kembali ke model.

Dalam contoh iCloudUIDoc\MonkeyDocumentViewController.cs menampilkan teks MonkeyDocument dalam UITextView. ViewDidLoad mendengarkan pemberitahuan yang dikirim dalam MonkeyDocument.LoadFromContents metode . LoadFromContents dipanggil saat iCloud memiliki data baru untuk file, sehingga pemberitahuan menunjukkan bahwa dokumen telah diperbarui.

NSNotificationCenter.DefaultCenter.AddObserver (this,
    new Selector ("dataReloaded:"),
    new NSString ("monkeyDocumentModified"),
    null
);

Penangan pemberitahuan kode sampel memanggil metode untuk memperbarui UI - dalam hal ini tanpa deteksi atau resolusi konflik.

[Export ("dataReloaded:")]
void DataReloaded (NSNotification notification)
{
    doc = (MonkeyDocument)notification.Object;
    // we just overwrite whatever was being typed, no conflict resolution for now
    docText.Text = doc.DocumentString;
}

Menyimpan Dokumen iCloud

Untuk menambahkan UIDocument ke iCloud, Anda dapat memanggil UIDocument.Save secara langsung (hanya untuk dokumen baru) atau memindahkan file yang ada menggunakan NSFileManager.DefaultManager.SetUbiquitious. Contoh kode membuat dokumen baru langsung di kontainer ubiquity dengan kode ini (ada dua handler penyelesaian di sini, satu untuk Save operasi dan yang lain untuk Open):

var docsFolder = Path.Combine (iCloudUrl.Path, "Documents"); // NOTE: Documents folder is user-accessible in Settings
var docPath = Path.Combine (docsFolder, MonkeyDocFilename);
var ubiq = new NSUrl (docPath, false);
var monkeyDoc = new MonkeyDocument (ubiq);
monkeyDoc.Save (monkeyDoc.FileUrl, UIDocumentSaveOperation.ForCreating, saveSuccess => {
Console.WriteLine ("Save completion:" + saveSuccess);
if (saveSuccess) {
    monkeyDoc.Open (openSuccess => {
        Console.WriteLine ("Open completion:" + openSuccess);
        if (openSuccess) {
            Console.WriteLine ("new document for iCloud");
            Console.WriteLine (" == " + monkeyDoc.DocumentString);
            viewController.DisplayDocument (monkeyDoc);
        } else {
            Console.WriteLine ("couldn't open");
        }
    });
} else {
    Console.WriteLine ("couldn't save");
}

Perubahan berikutnya pada dokumen tidak "disimpan" secara langsung, sebaliknya kami memberi tahu UIDocument bahwa dokumen telah berubah dengan UpdateChangeCount, dan secara otomatis akan menjadwalkan penyimpanan ke operasi disk:

doc.UpdateChangeCount (UIDocumentChangeKind.Done);

Mengelola Dokumen iCloud

Pengguna dapat mengelola dokumen iCloud di direktori Dokumen "kontainer ubiquity" di luar aplikasi Anda melalui Pengaturan; mereka dapat melihat daftar file dan menggesek untuk menghapus. Kode aplikasi harus dapat menangani situasi di mana dokumen dihapus oleh pengguna. Jangan simpan data aplikasi internal di direktori Dokumen .

Managing iCloud Documents workflow

Pengguna juga akan menerima peringatan yang berbeda ketika mereka mencoba menghapus aplikasi yang diaktifkan iCloud dari perangkat mereka, untuk memberi tahu mereka tentang status dokumen iCloud yang terkait dengan aplikasi tersebut.

Screenshot shows a warning for Document Updates Pending.

Screenshot shows a warning for Delete i Cloud.

Pencadangan iCloud

Meskipun mencadangkan ke iCloud bukanlah fitur yang diakses langsung oleh pengembang, cara Anda mendesain aplikasi dapat memengaruhi pengalaman pengguna. Apple menyediakan Panduan Penyimpanan Data iOS bagi pengembang untuk diikuti dalam aplikasi iOS mereka.

Pertimbangan yang paling penting adalah apakah aplikasi Anda menyimpan file besar yang tidak dihasilkan pengguna (misalnya, aplikasi pembaca majalah yang menyimpan ratusan-plus megabyte konten per masalah). Apple lebih suka Anda tidak menyimpan data semacam ini di mana data tersebut akan dicadangkan ke iCloud dan mengisi kuota iCloud pengguna secara tidak perlu.

Aplikasi yang menyimpan data dalam jumlah besar seperti ini harus menyimpannya di salah satu direktori pengguna yang tidak dicadangkan (misalnya. Cache atau tmp) atau gunakan NSFileManager.SetSkipBackupAttribute untuk menerapkan bendera ke file tersebut sehingga iCloud mengabaikannya selama operasi pencadangan.

Ringkasan

Artikel ini memperkenalkan fitur iCloud baru yang disertakan dalam iOS 5. Ini memeriksa langkah-langkah yang diperlukan untuk mengonfigurasi proyek Anda untuk menggunakan iCloud lalu memberikan contoh cara menerapkan fitur iCloud.

Contoh penyimpanan kunci-nilai menunjukkan bagaimana iCloud dapat digunakan untuk menyimpan sejumlah kecil data yang mirip dengan cara NSUserPreferences disimpan. Contoh UIDocument menunjukkan bagaimana data yang lebih kompleks dapat disimpan dan disinkronkan di beberapa perangkat melalui iCloud.

Akhirnya menyertakan diskusi singkat tentang bagaimana penambahan iCloud Backup harus memengaruhi desain aplikasi Anda.