Použití iCloudu s Xamarin.iOS

Rozhraní API úložiště iCloud v iOS 5 umožňuje aplikacím ukládat uživatelské dokumenty a data specifická pro aplikace do centrálního umístění a přistupovat k těmto položkám ze všech zařízení uživatele.

K dispozici jsou čtyři typy úložiště:

  • Úložiště klíč-hodnota – sdílení malých objemů dat s aplikací na jiných zařízeních uživatele

  • Úložiště UIDocument – k ukládání dokumentů a dalších dat v účtu iCloud uživatele pomocí podtřídy UIDocument.

  • CoreData – Úložiště databáze SQLite

  • Jednotlivé soubory a adresáře – pro správu velkého množství různých souborů přímo v systému souborů.

Tento dokument popisuje první dva typy – páry klíč-hodnota a podtřídy UIDocument – a jak tyto funkce používat v Xamarin.iOS.

Důležité

Apple poskytuje nástroje , které vývojářům pomáhají správně zvládnout obecné nařízení Evropské unie o ochraně osobních údajů (GDPR).

Požadavky

  • Nejnovější stabilní verze Xamarin.iOS
  • Xcode 10
  • Visual Studio pro Mac nebo Visual Studio 2019.

Příprava na vývoj na iCloudu

Aplikace musí být nakonfigurované tak, aby používaly iCloud na portálu Apple Provisioning i na samotném projektu. Před vývojem pro iCloud (nebo vyzkoušením ukázek) postupujte podle následujících kroků.

Správné konfigurace aplikace pro přístup k iCloudu:

  • Najděte ID vašeho týmu – přihlaste se k developer.apple.com a navštivte Centrum > členů Souhrn účtu pro vývojáře účtu > a získejte ID týmu (nebo INDIVIDUÁLNÍ ID pro jednotlivé vývojáře). Bude to řetězec 10 znaků ( například A93A5CM278 ) – tento řetězec je součástí "identifikátoru kontejneru".

  • Vytvořte nové ID aplikace – Pokud chcete vytvořit ID aplikace, postupujte podle kroků uvedených v části Provisioning for Store Technologies v průvodci Device Provisioningem a nezapomeňte zkontrolovat iCloud jako povolenou službu:

Check iCloud as an allowed service

  • Vytvořte nový profil zřizování – Chcete-li vytvořit zřizovací profil , postupujte podle kroků uvedených v průvodci zřizováním zařízení .

  • Přidejte identifikátor kontejneru do souboru Entitlements.plist – formát identifikátoru kontejneru je TeamID.BundleID. Další informace najdete v příručce Práce s nároky .

  • Konfigurace vlastností projektu – V souboru Info.plist se ujistěte, že identifikátor sady odpovídá ID sady prostředků nastavené při vytváření ID aplikace; Podepisování balíčků pro iOS používá zřizovací profil , který obsahuje ID aplikace se službou iCloud App Service a vybraný soubor vlastních nároků . To vše lze provést v sadě Visual Studio v podokně Vlastnosti projektu.

  • Povolte na svém zařízení iCloud – přejděte na Nastavení > iCloud a ujistěte se, že je zařízení přihlášené. Vyberte a zapněte možnost Dokumenty a data .

  • Zařízení musíte použít k otestování iCloudu – nebude fungovat na simulátoru. Ve skutečnosti potřebujete dvě nebo více zařízení přihlášených pomocí stejného Apple ID, abyste viděli iCloud v akci.

Úložiště klíč-hodnota

Úložiště klíč-hodnota je určeno pro malé objemy dat, které uživatel může chtít zachovat na různých zařízeních – například poslední stránku, kterou si prohlížel v knize nebo časopisu. Úložiště klíč-hodnota by se nemělo používat k zálohování dat.

Při používání úložiště klíč-hodnota je potřeba mít na paměti určitá omezení:

  • Maximální velikost klíče – Názvy klíčů nesmí být delší než 64 bajtů.

  • Maximální velikost hodnoty – Do jedné hodnoty nelze uložit více než 64 kilobajtů.

  • Maximální velikost úložiště klíč-hodnota pro aplikaci – Aplikace můžou celkem ukládat maximálně 64 kilobajtů dat klíč-hodnota. Pokusy o nastavení klíčů nad rámec tohoto limitu selžou a předchozí hodnota se zachová.

  • Datové typy – Ukládat lze pouze základní typy, jako jsou řetězce, čísla a logické hodnoty.

Příklad iCloudKeyValue ukazuje, jak funguje. Ukázkový kód vytvoří klíč pojmenovaný pro každé zařízení: tento klíč můžete nastavit na jednom zařízení a sledovat, jak se hodnota rozšíří do ostatních. Vytvoří také klíč s názvem "Sdílené", který se dá upravit na jakémkoli zařízení – pokud upravíte na mnoha zařízeních najednou, iCloud se rozhodne, jakou hodnotu "wins" (pomocí časového razítka na změně) a rozšíří se.

Tento snímek obrazovky ukazuje ukázku, která se používá. Když se oznámení o změnách dostanou z iCloudu, vytisknou se v textovém zobrazení pro posouvání v dolní části obrazovky a aktualizují se ve vstupních polích.

The flow of messages between devices

Nastavení a načítání dat

Tento kód ukazuje, jak nastavit řetězcovou hodnotu.

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

Volání synchronizace zajišťuje, že hodnota zůstane uložena pouze v úložišti místního disku. Synchronizace s iCloudem probíhá na pozadí a kód aplikace nemůže být vynucený. Při dobrém síťovém připojení k synchronizaci často dochází během 5 sekund, ale pokud je síť špatná (nebo odpojená), aktualizace může trvat mnohem déle.

Hodnotu můžete načíst pomocí tohoto kódu:

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

Hodnota se načte z místního úložiště dat – tato metoda se nepokoušá kontaktovat servery iCloud, aby získaly nejnovější hodnotu. iCloud aktualizuje místní úložiště dat podle vlastního plánu.

Odstranění dat

Pokud chcete úplně odebrat pár klíč-hodnota, použijte metodu Remove takto:

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

Sledování změn

Aplikace může také přijímat oznámení, když se hodnoty změní v iCloudu přidáním pozorovatele do objektu NSNotificationCenter.DefaultCenter. Následující kód z metody KeyValueViewController.csViewWillAppear ukazuje, jak naslouchat těmto oznámením, a vytvořit seznam klíčů, které byly změněny:

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...
});

Váš kód pak může provést nějakou akci se seznamem změněných klíčů, například aktualizovat místní kopii nebo aktualizovat uživatelské rozhraní novými hodnotami.

Možné důvody změn: ServerChange (0), InitialSyncChange (1) nebo QuotaViolationChange (2). V případě potřeby můžete získat přístup k důvodu a provést jiné zpracování (například v důsledku kvótyViolationChange budete muset některé klíče odebrat).

Úložiště dokumentů

ICloud Document Storage je navržený tak, aby spravuje data, která jsou důležitá pro vaši aplikaci (a pro uživatele). Dá se použít ke správě souborů a dalších dat, která vaše aplikace potřebuje ke spuštění, a současně poskytuje funkce zálohování a sdílení na základě iCloudu na všech zařízeních uživatele.

Tento diagram znázorňuje, jak všechno zapadá dohromady. Každé zařízení obsahuje data uložená v místním úložišti (UbiquityContainer) a iCloud Daemon operačního systému se stará o odesílání a přijímání dat v cloudu. Veškerý přístup k souboru ubiquityContainer musí být proveden přes FilePresenter/FileCoordinator, aby se zabránilo souběžnému přístupu. Třída UIDocument implementuje ty za vás; tento příklad ukazuje, jak používat UIDocument.

The document storage overview

Příklad iCloudUIDoc implementuje jednoduchou UIDocument podtřídu, která obsahuje jedno textové pole. Text se vykreslí v editoru UITextView a úpravy se rozšíří do jiných zařízení s červenou zprávou s oznámením. Ukázkový kód se nezabývá s pokročilejšími funkcemi iCloudu, jako je řešení konfliktů.

Tento snímek obrazovky ukazuje ukázkovou aplikaci – po změně textu a stisknutí UpdateChangeCount se dokument synchronizuje přes iCloud do jiných zařízení.

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

Ukázka iCloudUIDoc má pět částí:

  1. Přístup k ubiquityContainer – určete, jestli je povolený iCloud, a pokud ano cesta k oblasti úložiště iCloud vaší aplikace.

  2. Vytvoření podtřídy UIDocument – vytvoření třídy pro zprostředkující mezi úložištěm iCloud a objekty modelu.

  3. Hledání a otevírání dokumentů iCloudu – používejte NSFileManager a NSPredicate najděte dokumenty iCloudu a otevřete je.

  4. Zobrazení dokumentů iCloudu – zpřístupněte vlastnosti z vašeho UIDocument zařízení, abyste mohli pracovat s ovládacími prvky uživatelského rozhraní.

  5. Ukládání dokumentů iCloudu – zajistěte, aby změny provedené v uživatelském rozhraní zůstaly uložené na disku a iCloudu.

Všechny operace iCloudu běží (nebo by se měly spouštět) asynchronně, aby se při čekání na něco neblokovaly. V ukázce uvidíte tři různé způsoby, jak toho dosáhnout:

Vlákna - v AppDelegate.FinishedLaunching počátečním volání GetUrlForUbiquityContainer je provedeno na jiném vlákně, aby se zabránilo blokování hlavního vlákna.

NotificationCenter – registrace oznámení při asynchronních operacích, jako NSMetadataQuery.StartQuery je například dokončení

Obslužné rutiny dokončení – předávání metod ke spuštění při dokončení asynchronních operací, jako je UIDocument.Open.

Přístup k ubiquityContainer

Prvním krokem při používání úložiště dokumentů iCloudu je určit, jestli je povolený iCloud, a pokud ano umístění "všudypřítomného kontejneru" (adresář, ve kterém jsou soubory s podporou iCloudu uložené na zařízení).

Tento kód je v AppDelegate.FinishedLaunching metodě ukázky.

// 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;
});

I když ukázka to nedělá, Apple doporučuje volání GetUrlForUbiquityContainer pokaždé, když aplikace přijde do popředí.

Vytvoření podtřídy UIDocument

Všechny soubory a adresáře iCloud (tj. cokoli uložené v adresáři UbiquityContainer) musí být spravováno pomocí NSFileManager metod, implementace protokolu NSFilePresenter a zápisu přes NSFileCoordinator. Nejjednodušší způsob, jak to udělat, není psát sami, ale podtřídu UIDocument, která dělá vše za vás.

Existují pouze dvě metody, které je nutné implementovat v podtřídě UIDocument pro práci s iCloudem:

  • LoadFromContents – předá data NSData obsahu souboru, abyste mohli rozbalit třídu/es modelu.

  • ContentsForType – žádost o zadání reprezentace NSData třídy/es modelu pro uložení na disk (a cloud).

Tento ukázkový kód z iCloudUIDoc\MonkeyDocument.cs ukazuje, jak implementovat 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;
    }
}

Datový model v tomto případě je velmi jednoduchý – jedno textové pole. Datový model může být stejně složitý, jako je například dokument XML nebo binární data. Primární rolí implementace UIDocument je přeložit mezi třídami modelu a reprezentací NSData, která se dá uložit nebo načíst na disk.

Hledání a otevírání dokumentů iCloudu

Ukázková aplikace se zabývá pouze jedním souborem – test.txt – takže kód v AppDelegate.cs vytvoří NSPredicate a NSMetadataQuery bude vypadat speciálně pro tento název souboru. Po NSMetadataQuery dokončení se spustí asynchronně a odešle oznámení. DidFinishGathering získá volání pozorovatel oznámení, zastaví dotaz a volá LoadDocument, který používá UIDocument.Open metodu s obslužnou rutinou dokončení k pokusu o načtení souboru a zobrazení v MonkeyDocumentViewControllersouboru .

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
}

Zobrazení dokumentů iCloudu

Zobrazení souboru UIDocument by se nemělo lišit od žádné jiné třídy modelu – vlastnosti se zobrazí v ovládacích prvcích uživatelského rozhraní, případně je upraví uživatel a pak se zapíše zpět do modelu.

V příkladu iCloudUIDoc\MonkeyDocumentViewController.cs zobrazí text MonkeyDocument v souboru UITextView. ViewDidLoad naslouchá oznámení odeslané v MonkeyDocument.LoadFromContents metodě. LoadFromContents je volána, když iCloud obsahuje nová data pro soubor, takže oznámení indikuje, že dokument byl aktualizován.

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

Obslužná rutina oznámení vzorového kódu volá metodu pro aktualizaci uživatelského rozhraní – v tomto případě bez detekce konfliktů nebo řešení.

[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;
}

Ukládání dokumentů iCloudu

Pokud chcete přidat uiDocument do iCloudu, můžete volat UIDocument.Save přímo (pouze pro nové dokumenty) nebo přesunout existující soubor pomocí NSFileManager.DefaultManager.SetUbiquitious. Ukázkový kód vytvoří nový dokument přímo v kontejneru všudypřítomnosti s tímto kódem (tady jsou dva obslužné rutiny dokončení, jeden pro Save operaci a druhý pro otevření):

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");
}

Následné změny dokumentu se přímo neuloží, místo toho řekneme UIDocument , že se změnil UpdateChangeCountpomocí , a automaticky naplánuje operaci uložení na disk:

doc.UpdateChangeCount (UIDocumentChangeKind.Done);

Správa dokumentů iCloudu

Uživatelé můžou spravovat dokumenty iCloudu v adresáři Dokumenty "všudypřítomného kontejneru" mimo vaši aplikaci prostřednictvím Nastavení; můžou zobrazit seznam souborů a potáhnutím prstem odstranit. Kód aplikace by měl být schopný zvládnout situaci, kdy uživatel odstraní dokumenty. Neukládejte interní data aplikace do adresáře Dokumenty .

Managing iCloud Documents workflow

Uživatelům se při pokusu o odebrání aplikace s podporou iCloudu ze svého zařízení zobrazí také různá upozornění, aby je informovali o stavu dokumentů iCloudu souvisejících s danou aplikací.

Screenshot shows a warning for Document Updates Pending.

Screenshot shows a warning for Delete i Cloud.

Zálohování iCloudu

Zálohování na iCloud sice není funkce, ke které mají vývojáři přímý přístup, ale způsob, jakým navrhujete aplikaci, může mít vliv na uživatelské prostředí. Apple poskytuje pokyny pro úložiště dat pro iOS, aby vývojáři mohli sledovat své aplikace pro iOS.

Nejdůležitějším aspektem je, jestli vaše aplikace ukládá velké soubory, které nejsou generovány uživatelem (například aplikace čtenáře časopisu, která ukládá stovky megabajtů obsahu na jeden problém). Apple dává přednost tomu, že tento druh dat neukládáte tam, kde se budou zálohovat do iCloudu, a zbytečně vyplňte kvótu iCloudu uživatele.

Aplikace, které ukládají velké objemy dat, by je měly ukládat do jednoho z uživatelských adresářů, které nejsou zálohované (např. Mezipaměti nebo tmp) nebo použití NSFileManager.SetSkipBackupAttribute příznaku u těchto souborů, aby je iCloud při operacích zálohování ignoroval.

Shrnutí

Tento článek představil novou funkci iCloudu, která je součástí iOS 5. Prozkoumal kroky potřebné ke konfiguraci projektu tak, aby používal iCloud, a pak poskytl příklady implementace funkcí iCloudu.

Příklad úložiště klíč-hodnota ukazuje, jak lze iCloud použít k ukládání malého množství dat podobně jako NSUserPreferences. Příklad UIDocument ukázal, jak složitější data je možné ukládat a synchronizovat napříč několika zařízeními prostřednictvím iCloudu.

Nakonec obsahovala stručnou diskuzi o tom, jak by mělo přidání zálohování iCloudu ovlivnit návrh vaší aplikace.