Używanie usługi iCloud z platformą Xamarin.iOS
Interfejs API magazynu usługi iCloud w systemie iOS 5 umożliwia aplikacjom zapisywanie dokumentów użytkownika i danych specyficznych dla aplikacji w centralnej lokalizacji oraz uzyskiwanie dostępu do tych elementów ze wszystkich urządzeń użytkownika.
Dostępne są cztery typy magazynu:
Magazyn klucz-wartość — aby udostępniać aplikacji małe ilości danych na innych urządzeniach użytkownika.
Magazyn UIDocument — do przechowywania dokumentów i innych danych na koncie użytkownika w usłudze iCloud przy użyciu podklasy UIDocument.
CoreData — magazyn bazy danych SQLite.
Poszczególne pliki i katalogi — do zarządzania wieloma różnymi plikami bezpośrednio w systemie plików.
W tym dokumencie omówiono dwa pierwsze typy — pary klucz-wartość i podklasy UIDocument — oraz sposób używania tych funkcji na platformie Xamarin.iOS.
Ważne
Firma Apple udostępnia narzędzia pomagające deweloperom w prawidłowym obsłudze ogólnego rozporządzenia o ochronie danych (RODO) Unii Europejskiej.
Wymagania
- Najnowsza stabilna wersja platformy Xamarin.iOS
- Xcode 10
- Visual Studio dla komputerów Mac lub Visual Studio 2019.
Przygotowywanie do programowania w usłudze iCloud
Aplikacje muszą być skonfigurowane tak, aby korzystały z usługi iCloud zarówno w portalu aprowizacji firmy Apple, jak i w samym projekcie. Przed rozpoczęciem tworzenia aplikacji dla usługi iCloud (lub wypróbowanie przykładów) wykonaj poniższe kroki.
Aby poprawnie skonfigurować aplikację w celu uzyskania dostępu do usługi iCloud:
Znajdź identyfikator teamID — zaloguj się, aby developer.apple.com i odwiedź Centrum > członkowskie Podsumowanie konta dewelopera konta>, aby uzyskać identyfikator zespołu (lub identyfikator indywidualny dla pojedynczych deweloperów). Będzie to ciąg 10 znaków ( na przykład A93A5CM278 ) — stanowi to część "identyfikatora kontenera".
Utwórz nowy identyfikator aplikacji — aby utworzyć identyfikator aplikacji, wykonaj kroki opisane w sekcji Provisioning for Store Technologies (Aprowizowanie technologii sklepu) przewodnika Device Provisioning (Aprowizacja urządzeń) i pamiętaj, aby sprawdzić usługę iCloud jako dozwoloną usługę:
Utwórz nowy profil aprowizacji — aby utworzyć profil aprowizacji, wykonaj kroki opisane w przewodniku Device Provisioning .
Dodaj identyfikator kontenera do pliku Entitlements.plist — format identyfikatora kontenera to
TeamID.BundleID
. Aby uzyskać więcej informacji, zapoznaj się z przewodnikiem Praca z upoważnieniami .Skonfiguruj właściwości projektu — w pliku Info.plist upewnij się, że identyfikator pakietu jest zgodny z zestawem Identyfikator pakietu podczas tworzenia identyfikatora aplikacji; Podpisywanie pakietu systemu iOS używa profilu aprowizacji zawierającego identyfikator aplikacji z usługą App Service w usłudze iCloud oraz wybrany plik uprawnień niestandardowych. Wszystko to można zrobić w programie Visual Studio w okienku Właściwości projektu.
Włącz usługę iCloud na urządzeniu — przejdź do pozycji Ustawienia > w usłudze iCloud i upewnij się, że urządzenie jest zalogowane. Wybierz i włącz opcję Dokumenty i dane .
Aby przetestować usługę iCloud , musisz użyć urządzenia — nie będzie ono działać w symulatorze. W rzeczywistości naprawdę potrzebujesz co najmniej dwóch urządzeń zalogowanych przy użyciu tego samego identyfikatora Apple ID, aby zobaczyć działanie usługi iCloud.
Magazyn klucz-wartość
Magazyn klucz-wartość jest przeznaczony dla małych ilości danych, które użytkownik może lubić utrwalone na różnych urządzeniach , takich jak ostatnia strona, która została wyświetlona w książce lub magazynie. Magazyn klucz-wartość nie powinien być używany do tworzenia kopii zapasowych danych.
Podczas korzystania z magazynu klucz-wartość należy pamiętać o pewnych ograniczeniach:
Maksymalny rozmiar klucza — nazwy kluczy nie mogą być dłuższe niż 64 bajty.
Maksymalny rozmiar wartości — nie można przechowywać więcej niż 64 kilobajtów w jednej wartości.
Maksymalny rozmiar magazynu klucz-wartość dla aplikacji — aplikacje mogą przechowywać maksymalnie 64 kilobajty danych klucz-wartość łącznie. Próby ustawienia kluczy poza tym limitem zakończy się niepowodzeniem, a poprzednia wartość będzie się powtarzać.
Typy danych — można przechowywać tylko podstawowe typy, takie jak ciągi, liczby i wartości logiczne.
Przykład iCloudKeyValue pokazuje, jak działa. Przykładowy kod tworzy klucz o nazwie dla każdego urządzenia: możesz ustawić ten klucz na jednym urządzeniu i obserwować propagację wartości do innych. Tworzy również klucz o nazwie "Udostępnione", który może być edytowany na dowolnym urządzeniu — jeśli edytujesz na wielu urządzeniach jednocześnie, usługa iCloud zdecyduje, która wartość "wygrywa" (przy użyciu znacznika czasu zmiany) i zostanie rozpropagowana.
Ten zrzut ekranu przedstawia przykład w użyciu. Gdy powiadomienia o zmianie są odbierane z usługi iCloud, są drukowane w widoku tekstowym przewijania w dolnej części ekranu i aktualizowane w polach wejściowych.
Ustawianie i pobieranie danych
Ten kod pokazuje, jak ustawić wartość ciągu.
var store = NSUbiquitousKeyValueStore.DefaultStore;
store.SetString("testkey", "VALUE IN THE CLOUD"); // key and value
store.Synchronize();
Wywołanie synchronizacji gwarantuje, że wartość jest utrwalana tylko w magazynie na dysku lokalnym. Synchronizacja z usługą iCloud odbywa się w tle i nie może być "wymuszona" przez kod aplikacji. W przypadku dobrej łączności sieciowej synchronizacja będzie często wykonywana w ciągu 5 sekund, jednak jeśli sieć jest słaba (lub rozłączona), aktualizacja może trwać znacznie dłużej.
Wartość można pobrać za pomocą tego kodu:
var store = NSUbiquitousKeyValueStore.DefaultStore;
display.Text = store.GetString("testkey");
Wartość jest pobierana z lokalnego magazynu danych — ta metoda nie próbuje skontaktować się z serwerami usługi iCloud w celu uzyskania wartości "latest". Usługa iCloud zaktualizuje lokalny magazyn danych zgodnie z własnym harmonogramem.
Usuwanie danych
Aby całkowicie usunąć parę klucz-wartość, użyj metody Remove w następujący sposób:
var store = NSUbiquitousKeyValueStore.DefaultStore;
store.Remove("testkey");
store.Synchronize();
Obserwowanie zmian
Aplikacja może również odbierać powiadomienia, gdy wartości są zmieniane przez usługę iCloud, dodając obserwatora do elementu NSNotificationCenter.DefaultCenter
.
Poniższy kod z metody KeyValueViewController.cs ViewWillAppear
pokazuje, jak nasłuchiwać tych powiadomień i utworzyć listę kluczy, które zostały zmienione:
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...
});
Kod może następnie wykonać jakąś akcję z listą zmienionych kluczy, na przykład zaktualizować lokalną kopię lub zaktualizować interfejs użytkownika przy użyciu nowych wartości.
Możliwe przyczyny zmiany to: ServerChange (0), InitialSyncChange (1) lub QuotaViolationChange (2). Możesz uzyskać dostęp do przyczyny i wykonać inne przetwarzanie, jeśli jest to wymagane (na przykład może być konieczne usunięcie niektórych kluczy w wyniku elementu QuotaViolationChange).
Magazyn dokumentów
Usługa iCloud Document Storage została zaprojektowana tak, aby zarządzać danymi, które są ważne dla aplikacji (i dla użytkownika). Może służyć do zarządzania plikami i innymi danymi, które aplikacja musi uruchomić, jednocześnie zapewniając funkcję tworzenia kopii zapasowych i udostępniania na wszystkich urządzeniach użytkownika w usłudze iCloud.
Na tym diagramie pokazano, jak wszystko pasuje do siebie. Każde urządzenie ma dane zapisane w magazynie lokalnym (UbiquityContainer), a demon systemu operacyjnego iCloud zajmuje się wysyłaniem i odbieraniem danych w chmurze. Aby uniemożliwić współbieżny dostęp do pliku UbiquityContainer, należy wykonać za pośrednictwem klasy FilePresenter/FileCoordinator. Klasa implementuje te elementy. W UIDocument
tym przykładzie pokazano, jak używać narzędzia UIDocument.
Przykład iCloudUIDoc implementuje prostą UIDocument
podklasę zawierającą jedno pole tekstowe. Tekst jest renderowany w elemecie UITextView
i edycje są propagowane przez usługę iCloud do innych urządzeń z komunikatem z powiadomieniem wyświetlanym na czerwono. Przykładowy kod nie zajmuje się bardziej zaawansowanymi funkcjami usługi iCloud, takimi jak rozwiązywanie konfliktów.
Ten zrzut ekranu przedstawia przykładową aplikację — po zmianie tekstu i naciśnięciu UpdateChangeCount dokument jest synchronizowany za pośrednictwem usługi iCloud z innymi urządzeniami.
Przykład iCloudUIDoc to pięć części:
Uzyskiwanie dostępu do aplikacji UbiquityContainer — określanie, czy usługa iCloud jest włączona, a jeśli tak, ścieżka do obszaru magazynu aplikacji w usłudze iCloud.
Tworzenie podklasy UIDocument — tworzenie klasy pośredniej między magazynem iCloud a obiektami modelu.
Znajdowanie i otwieranie dokumentów w usłudze iCloud — służy
NSFileManager
NSPredicate
do znajdowania i otwierania dokumentów w usłudze iCloud.Wyświetlanie dokumentów w usłudze iCloud — uwidacznianie właściwości z poziomu kontrolki
UIDocument
interfejsu użytkownika.Zapisywanie dokumentów w usłudze iCloud — upewnij się, że zmiany wprowadzone w interfejsie użytkownika są utrwalane na dysku i w usłudze iCloud.
Wszystkie operacje w usłudze iCloud są uruchamiane (lub powinny być uruchamiane) asynchronicznie, aby nie blokowały się podczas oczekiwania na coś się stało. W przykładzie zobaczysz trzy różne sposoby wykonania tego zadania:
Wątki — w początkowym wywołaniu elementu GetUrlForUbiquityContainer
jest wykonywane w AppDelegate.FinishedLaunching
innym wątku, aby zapobiec blokowaniu głównego wątku.
NotificationCenter — rejestrowanie w celu otrzymywania powiadomień, gdy operacje asynchroniczne, takie jak NSMetadataQuery.StartQuery
ukończone.
Procedury obsługi uzupełniania — przekazywanie metod do uruchamiania po zakończeniu operacji asynchronicznych, takich jak UIDocument.Open
.
Uzyskiwanie dostępu do ubiquityContainer
Pierwszym krokiem korzystania z usługi iCloud Document Storage jest określenie, czy usługa iCloud jest włączona, a jeśli tak, lokalizacja "kontenera wszechobecności" (katalog, w którym pliki z włączoną obsługą usługi iCloud są przechowywane na urządzeniu).
Ten kod jest w AppDelegate.FinishedLaunching
metodzie przykładu.
// 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;
});
Mimo że przykład nie robi tego, firma Apple zaleca wywołanie metody GetUrlForUbiquityContainer za każdym razem, gdy aplikacja przychodzi na pierwszy plan.
Tworzenie podklasy UIDocument
Wszystkie pliki i katalogi iCloud (tj. wszystkie elementy przechowywane w katalogu UbiquityContainer) muszą być zarządzane przy użyciu metod NSFileManager, implementowania protokołu NSFilePresenter i zapisywania za pośrednictwem NSFileCoordinator. Najprostszym sposobem na zrobienie tego wszystkiego nie jest samodzielne napisanie go, ale podklasa UIDocument, która robi to wszystko dla Ciebie.
Istnieją tylko dwie metody, które należy zaimplementować w podklasie UIDocument, aby pracować z usługą iCloud:
LoadFromContents — przekazuje dane NSData zawartości pliku do rozpakowywania do klasy/es modelu.
ContentsForType — żądanie podania reprezentacji NSData klasy/es modelu w celu zapisania na dysku (i w chmurze).
Ten przykładowy kod z witryny iCloudUIDoc\MonkeyDocument.cs pokazuje, jak zaimplementować dokument 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 danych w tym przypadku jest bardzo prosty — jedno pole tekstowe. Model danych może być tak złożony, jak to wymagane, na przykład dokument XML lub dane binarne. Podstawową rolą implementacji UIDocument jest tłumaczenie między klasami modelu a reprezentacją NSData, którą można zapisać/załadować na dysku.
Znajdowanie i otwieranie dokumentów w usłudze iCloud
Przykładowa aplikacja zajmuje się tylko jednym plikiem — test.txt — więc kod w AppDelegate.cs tworzy element NSPredicate
i NSMetadataQuery
, aby wyszukać specjalnie dla tej nazwy pliku. Polecenie NSMetadataQuery
jest uruchamiane asynchronicznie i wysyła powiadomienie po zakończeniu. DidFinishGathering
program jest wywoływany przez obserwatora powiadomień, zatrzymuje zapytanie i wywołuje metodę LoadDocument, która używa UIDocument.Open
metody z procedurą obsługi uzupełniania, aby spróbować załadować plik i wyświetlić go w obiekcie 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
}
Wyświetlanie dokumentów usługi iCloud
Wyświetlanie dokumentu UIDocument nie powinno różnić się od innych klas modelu — właściwości są wyświetlane w kontrolkach interfejsu użytkownika, ewentualnie edytowane przez użytkownika, a następnie zapisywane z powrotem do modelu.
W przykładzie iCloudUIDoc\MonkeyDocumentViewController.cs wyświetla tekst MonkeyDocument w obiekcie UITextView
. ViewDidLoad
nasłuchuje powiadomienia wysłanego w metodzie MonkeyDocument.LoadFromContents
. LoadFromContents
Jest wywoływany, gdy usługa iCloud ma nowe dane dla pliku, dzięki czemu powiadomienie wskazuje, że dokument został zaktualizowany.
NSNotificationCenter.DefaultCenter.AddObserver (this,
new Selector ("dataReloaded:"),
new NSString ("monkeyDocumentModified"),
null
);
Przykładowa procedura obsługi powiadomień kodu wywołuje metodę w celu zaktualizowania interfejsu użytkownika — w tym przypadku bez żadnego wykrywania lub rozwiązywania konfliktów.
[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;
}
Zapisywanie dokumentów w usłudze iCloud
Aby dodać dokument UIDocument do usługi iCloud, możesz wywołać UIDocument.Save
bezpośrednio (tylko w przypadku nowych dokumentów) lub przenieść istniejący plik przy użyciu polecenia NSFileManager.DefaultManager.SetUbiquitious
. Przykładowy kod tworzy nowy dokument bezpośrednio w kontenerze wszechobecności za pomocą tego kodu (w tym miejscu istnieją dwa programy obsługi uzupełniania: jeden dla operacji, a drugi dla Save
operacji 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");
}
Kolejne zmiany w dokumencie nie są zapisywane bezpośrednio, zamiast tego informujemy UIDocument
, że zmieniono UpdateChangeCount
go za pomocą polecenia i automatycznie zaplanuje operację zapisywania na dysku:
doc.UpdateChangeCount (UIDocumentChangeKind.Done);
Zarządzanie dokumentami w usłudze iCloud
Użytkownicy mogą zarządzać dokumentami usługi iCloud w katalogu Documents "wszechobecności" poza aplikacją za pomocą ustawień. Mogą wyświetlać listę plików i szybko przesuwać palcem w celu usunięcia. Kod aplikacji powinien mieć możliwość obsługi sytuacji, w której dokumenty są usuwane przez użytkownika. Nie należy przechowywać danych aplikacji wewnętrznych w katalogu Documents .
Użytkownicy będą również otrzymywać różne ostrzeżenia podczas próby usunięcia aplikacji z obsługą usługi iCloud z urządzenia, aby poinformować ich o stanie dokumentów usługi iCloud związanych z aplikacją.
Kopia zapasowa usługi iCloud
Chociaż tworzenie kopii zapasowej w usłudze iCloud nie jest funkcją, która jest bezpośrednio uzyskiwana przez deweloperów, sposób projektowania aplikacji może mieć wpływ na środowisko użytkownika. Firma Apple udostępnia deweloperom wytyczne dotyczące magazynu danych dla systemu iOS, które mają być zgodne z aplikacjami systemu iOS.
Najważniejszą kwestią jest to, czy aplikacja przechowuje duże pliki, które nie są generowane przez użytkownika (na przykład aplikacja czytelnika magazynu, która przechowuje setki megabajtów zawartości na problem). Firma Apple preferuje, aby nie przechowywać tego rodzaju danych, w których kopia zapasowa zostanie utworzona w usłudze iCloud i niepotrzebnie wypełni limit przydziału użytkownika w usłudze iCloud.
Aplikacje przechowujące duże ilości danych, takie jak te, powinny przechowywać je w jednym z katalogów użytkowników, których kopia zapasowa nie jest tworzona (np. Pamięci podręczne lub tmp) lub użyj polecenia NSFileManager.SetSkipBackupAttribute
, aby zastosować flagę do tych plików, aby usługa iCloud ignorowała je podczas operacji tworzenia kopii zapasowych.
Podsumowanie
W tym artykule wprowadzono nową funkcję usługi iCloud zawartą w systemie iOS 5. Zbadano kroki wymagane do skonfigurowania projektu pod kątem używania usługi iCloud, a następnie podano przykłady implementowania funkcji usługi iCloud.
W przykładzie magazynu klucz-wartość pokazano, jak można użyć usługi iCloud do przechowywania niewielkiej ilości danych podobnych do sposobu przechowywania NSUserPreferences. W przykładzie UIDocument pokazano, jak bardziej złożone dane można przechowywać i synchronizować na wielu urządzeniach za pośrednictwem usługi iCloud.
Na koniec zawarto krótką dyskusję na temat tego, jak dodanie kopii zapasowej w usłudze iCloud powinno mieć wpływ na projekt aplikacji.