Arbetar med iCloud Dokumentlagring

Att hålla applikationsdata synkroniserad över enheter är en komplex och skrämmande uppgift. Lyckligtvis är det precis därför Apple byggde iCloud. I denna Tuts + Premium-serie kommer du att lära dig hur iCloud fungerar och hur dina applikationer kan dela data på flera enheter sömlöst.


Finns även i serien:

  1. Arbeta med iCloud: Introduktion
  2. Arbeta med iCloud: Key-Value Storage
  3. Arbetar med iCloud: Dokumentlagring
  4. Arbetar med iCloud: Core Data Integration

I den andra delen av denna serie visade jag dig hur du utnyttjar iClouds nyckelvärdeslagring för att hålla små mängder användardata synkroniserade över flera enheter. Även om Key-Value Storage är lätt att använda och adoptera, är en av nedstegen den begränsning det innebär för den mängd data som kan lagras. Kom ihåg att varje applikation endast kan lagra 1MB data och antalet nyckelvärdespar är begränsat till 1024. Som jag nämnde i slutet av den tidigare handledningen kan vår bokmärkeshanterare komma in i denna begränsning om någon av våra användare vill lagra många bokmärken.

Lösningen på detta problem växlar från iCloud Key Value Storage till iCloud Document Storage för att lagra bokmärken. När det gäller diskutrymme begränsas iCloud Document Storage endast av användarens iCloud-lagring. Att veta att ett gratis konto levereras med 5 GB datalagring, är iCloud Document Storage den perfekta lösningen för vår bokmärkeschef. I den här handledningen kommer vi att reflektera bokmärkeshanteraren från att använda iCloud Key Value Storage för att anta iCloud Document Storage.


Innan vi börjar

Jag vill betona att det är viktigt att du har läst första och andra avbetalningen i denna serie innan du läser det här stycket. I denna handledning kommer vi att refactor vår bokmärkeschef genom att bygga på de fundament vi lade i del 2 i serien.


Ett par ord om UIDocument

Med introduktionen av iCloud gjorde Apple också UIDocument tillgänglig för utvecklare. Ingenjörerna på Apple skapade UIDocument med iCloud i åtanke. UIDocument gör iCloud integration för dokumentbaserad applikation mycket enklare. Det är dock viktigt att notera det UIDocument gör mycket mer än att tillhandahålla ett lättanvänt API för iCloud-integration.

Dokumentbaserade applikationer måste hantera ett antal utmaningar, såsom (1) läsa och skriva data från och till disk utan att blockera användargränssnittet, (2) spara data till disk med lämpliga intervall och (3) eventuellt integrera med iCloud. UIDocument ger inbyggda lösningar för dessa utmaningar.

Innan vi börjar jobba med UIDocument, Jag vill klargöra vad UIDocument är och vad det inte är. UIDocument är ett kontrollerobjekt som hanterar en eller flera modeller precis som UIViewController styr och hanterar en eller flera visningar. UIDocument lagrar inte några data, men hanterar modellobjekten som håller användarens data. Detta är ett viktigt begrepp att förstå, vilket blir tydligare när vi börjar refactoring vår applikation att använda UIDocument.

Ett annat viktigt koncept att förstå är hur läs- och skrivoperationer fungerar när de används UIDocument. Om du väljer att använda UIDocument i din ansökan behöver du inte oroa dig för att blockera huvudtråden när du läser eller skriver data till disken. När man använder UIDocument, operativsystemet hanterar automatiskt ett antal uppgifter för dig i en bakgrundskö och ser till att huvudtråden fortfarande är mottaglig. Jag skulle vilja ta en stund och förklara varje operation mer ingående för att ge dig en bra förståelse för de olika rörliga delarna som är inblandade.

Låt oss börja med att läsa data från en skiva. Läsningsoperationen startas av en öppen operation initierad i anropskön. Den öppna operationen initieras när ett dokument öppnas genom att skicka ett meddelande till openWithCompletionHandler:. Vi skickar en färdighetshandlare som är påkallad när hela läsoperationen är klar. Detta är en viktig aspekt av läs- och skrivoperationerna. Det kan ta en icke-trivial tid att läsa eller skriva data från eller till disk, och vi vill inte göra detta på huvudtråden och blockera användargränssnittet. Den faktiska läsoperationen sker i en bakgrundskö som hanteras av operativsystemet. När läsoperationen avslutas, loadFromContents: ofType: error: Metoden kallas på dokumentet. Denna metod skickas UIDocument De data som behövs för att initiera den eller de modeller som den hanterar. Avslutningsbehandlaren åberopas när processen avslutas, vilket innebär att vi kan svara på laddningen av dokumentet genom att till exempel uppdatera användargränssnittet med innehållet i dokumentet.


Skrivoperationen är liknande. Det börjar med en spara operation som initieras i anropskön genom att skicka saveToURL: forSaveOperation: completionHandler: till dokumentobjektet. Liksom vid läsoperationen skickar vi en färdighetshandlare som påkallas när skrivoperationen avslutas. Skriva data till skivan sker i en bakgrundskö. Operativsystemet frågar UIDocument för en ögonblicksbild av dess modelldata genom att skicka den ett meddelande till contentsForType: error:. Avslutningsbehandlaren åberopas när skrivoperationen avslutas, vilket ger oss möjlighet att uppdatera användargränssnittet.


UIDocument är en basklass och är inte avsedd att användas direkt. Vi behöver subklass UIDocument och anpassa det till våra behov. Med andra ord, vi underklass UIDocument så att den vet om vår modell och hur man hanterar den. I sin mest grundläggande form, subclassing UIDocument kräver bara att vi åsidosätter loadFromContents: ofType: error: för läsning och contentsForType: error: för skrivande.

Förvirrad? Du borde vara. Även om UIDocument gör livet mycket lättare, det är en avancerad klass och vi hanterar ett komplext ämne. Jag är emellertid övertygad om att du kommer att få ett bra grepp om dokumentbaserade applikationer när vi har refactored vår ansökan.

Innan vi fortsätter vill jag klargöra vad våra mål för denna handledning är. Det primära målet är att refactor vår ansökan för att utnyttja iCloud Document Storage istället för iCloud Key Value Storage. Det betyder att vi kommer att utnyttja UIDocument och subclass det för att passa våra behov. Dessutom ska vi skapa en anpassad modell klass för vårt bokmärke som kommer att användas och hanteras av UIDocument underklass.


Steg 1: Konfigurera rättigheter

För närvarande är vår applikation konfigurerad att bara använda nyckelvärdeslagring. För att aktivera dokumentlagring måste vi först konfigurera programmets rättigheter. Öppna Målredigerare genom att välja vår ansökan i Project Navigator och välj det enda målet från mållistan. I ersättningar avsnitt ska du se iCloud-behållare Nedan iCloud Key Value Store. Listan bredvid iCloud-behållare är tom för tillfället. Klicka på plusknappen längst ner i listan och du kommer att se att Xcode skapar en iCloud-containeridentifierare för dig som matchar din appication-buntidentifierare.

Vad är en iCloud-behållare? Som namnet antyder är det en behållare i användarens iCloud-datalagring. Genom att ange en (eller flera) iCloud-behållare i vår applikations rättigheter fil, berättar vi operativsystemet vilka behållare vår ansökan har tillgång till.



Steg 2: Skapa bokmärkesklassen

I den tidigare handledningen lagrade vi varje bokmärke som en förekomst av NSDictionary, men det här är inte en bra lösning för en dokumentbaserad applikation. I stället skapar vi en anpassad bokmärkes klass som gör att vi enkelt kan arkivera dess data.

Skapa en ny NSObject underklass genom att välja Fil från menyn, välj Ny, och då Fil… . Välj Kakao Touch från den vänstra panelen och välj Mål-C-klass från listan med mallar till höger. Ge klassen ett namn på Bokmärke och se till att det är en underklass av NSObject. Ange var du vill spara den nya klassen och tryck på Skapa.



I vår modellens headerfil lägger vi till de två egenskaperna i bokmärkesmodellen som vi använde i den tidigare handledningen, a namn och a URL. Båda egenskaperna är instanser av NSString. Vi förklarar också en utsettsinitierare, som tar namnet och en URL som dess parametrar. Slutligen är det viktigt att se till att vår Bokmärke klassen överensstämmer med NSCoding protokoll.

 #importera  @interface Bookmark: NSObject  NSString * _name; NSString * _url;  @property (nonatomic, copy) NSString * namn; @property (nonatomic, copy) NSString * url; - (id) initWithName: (NSString *) namn ochURL: (NSString *) url; @slutet

I vår modells implementeringsfil definierar vi först två konstanter för vårt bokmärkes namn och webbadress. Det här är en bra pratice eftersom det kommer att minimera chansen att vi misstänker de nycklar som vi kommer att använda inom kort. Därefter implementerar vi vår initialiseringsmetod. Denna metod är enkel. Vi initierar vår förekomst och tilldelar namn och webbadress till våra variabler för bokmärkesinstans.

Den viktiga delen är att genomföra de nödvändiga metoderna för att göra Bokmärke klassen överensstämmer med NSCoding protokoll. Om NSCoding protokollet är nytt för dig, då uppmanar jag dig att läsa Programmeringsguiden för arkiv och serialiseringar, eftersom det här är ett viktigt ämne för alla kakao-touch-utvecklare. Kärnan i det är att NSCoding protokollet tillåter oss att enkelt arkivera och unarchive instanser av Bokmärke klass.

 #import "Bookmark.h" #define kBookmarkName @ "Bokmärkesnamn" #define kBookmarkURL @ "Bookmark URL" @implementation Bokmärke @synthesize name = _name, url = _url; #pragma mark - #pragma markera Initialization - (id) initWithName: (NSString *) namn ochURL: (NSString *) url self = [super init]; om (själv) self.name = name; self.url = url;  återvänd själv  #pragma markera - #pragma markera NSCoding Protocol - (void) encodeWithCoder: (NSCoder *) kodare [koder encodeObject: self.name forKey: kBookmarkName]; [koder encodeObject: self.url forKey: kBookmarkURL];  - (id) initWithCoder: (NSCoder *) kodare self = [super init]; om (self! = nil) self.name = [coder decodeObjectForKey: kBookmarkName]; self.url = [kodare avkodaObjectForKey: kBookmarkURL];  återvänd själv  @slutet

Steg 3: Subclassing UIDocument

Subclassing UIDocument är inte så svårt som du kanske tror. Som jag nämnde tidigare är allt vi behöver göra att åsidosätta två metoder och skapa en egenskap för bokmärkesmodellen som den kommer att hantera.

Skapa en ny klass precis som vi gjorde för vår Bokmärke klass och ge det ett namn på BookmarkDocument. Se till att det är en underklass av UIDocument. I huvudfilen till vår UIDocument underklass, lägger vi till en framåtriktad deklaration för Bokmärke klass och vi skapar en egenskap för vår bokmärkesmodell. Glöm inte att syntetisera accessors för den här egenskapen.

 #importera  @ klassbokmärke; @interface BookmarkDocument: UIDocument Bookmark * _bookmark;  @property (nonatomic, strong) Bokmärke * bokmärke; @slutet

I implementeringsfilen importerar vi huvudfilen till Bokmärke klass och definiera en annan konstant för att fungera som nyckeln för arkivering och arkivering av bokmärkesmodellen. Som jag nämnde tidigare behöver vi bara åsidosätta två metoder i UIDocument underklass, (1) loadFromContents: ofType: error: och (2) contentsForType: error:. Den första metoden kommer att åberopas när vi öppnar ett dokument medan den andra metoden kommer att åberopas när ett dokument sparas. Båda metoderna heter av operativsystemet. Vi behöver aldrig ringa dessa mehods direkt.

 #import "BookmarkDocument.h" #import "Bookmark.h" #define kArchiveKey @ "Bokmärke" @implementation BookmarkDocument @synthesize bookmark = _bookmark;

Låt mig gå igenom dig loadFromContents: ofType: error:. Det första argumentet är innehåll av typ id. Detta kan antingen vara en förekomst av NSData eller NSFileWrapper. Det senare är endast tillämpligt när applikationen använder filpaket. I vårt fall kan vi förvänta oss en NSData exempel. Vi kontrollerar först om längden på NSData exemplet är inte lika med noll. Vi initierar en instans av NSKeyedUnarchiver och leverera den med innehåll objekt. Genom att avkoda data får vi en instans av Bokmärke klass tillbaka. Detta är anledningen till att Bokmärke klassen överensstämmer med NSCoding protokoll. Om längden på NSData Instans är lika med noll, vi initierar ett nytt bokmärke med standardvärden för namn och URL. Observera att vi återvänder JA vid slutet av metoden.

 - (BOOL) loadFromContents: (id) content ofType: (NSString *) typnamnfel: (NSError * __ autoreleasing *) outError if ([innehållslängd]> 0) NSKeyedUnarchiver * unarchiver = [[NSKeyedUnarchiver alloker] initForReadingWithData: contents]; self.bookmark = [unarchiver decodeObjectForKey: kArchiveKey]; [unarchiver finishDecoding];  else self.bookmark = [[Bookmark alloc] initWithName: @ "Bookmark Name" ochURL: @ "www.example.com"];  returnera JA; 

De contentsForType: error: metod gör motsatsen. Det vill säga, vi levererar de data som behöver skrivas till disken. Detta dataobjekt är den så kallade ögonblicksbilden av våra modelldata. Vi gör detta genom att initiera en instans av NSMutableData och använd detta för att initiera en instans av NSKeyedArchiver. Vi kan sedan arkivera vår bokmärkesinstans så att den kan skrivas till disk. Denna metod förväntar oss att återkomma en förekomst av NSData och det är precis vad vi gör. Vår UIDocument Underklassen är nu klar för oss att använda.

 - (id) contentForType: (NSString *) typnamnfel: (NSError * __ autoreleasing *) outError NSMutableData * data = [[NSMutableData-allokering] init]; NSKeyedArchiver * archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData: data]; [arkivera encodeObject: self.bookmark forKey: kArchiveKey]; [arkivera finishEncoding]; returnera data; 

Steg 4: Refactoring Visa Controllers

Det finns fyra delar av vår applikation som behöver refactored om vi vill använda sig av iCloud Document Storage:
(1) ladda, (2) visa, (3) spara och (4) radera bokmärken. Låt oss börja med att läsa in bokmärken.

Innan vi tittar på loadBookmarks metod, vi måste förklara en privat egendom, en förekomst av NSMetadataQuery. Det kommer att bli klart varför vi behöver göra detta på bara några minuter. Glöm inte att lägga till två extra importdeklarationer till vår visningskontrollers implementeringsfil, en för Bokmärke klass och en för BookmarkDocument klass.

 #import "ViewController.h" #import "Bookmark.h" #import "BookmarkDocument.h" #import "AddBookmarkViewController.h" @interface ViewController () NSMetadataQuery * _query;  @property (nonatomic, strong) NSMetadataQuery * fråga; @end @implementation ViewController @synthesize bookmarks = _bookmarks; @synthesize tableView = _tableView; @synthesize query = _query;

Steg 4A: Laddar bokmärken

Istället för NSDictionary instanser, vår bokmärkes array, kommer datakällan i vår tabellvy att innehålla instanser av BookmarkDocument klass. Låt oss ta en titt på refactored loadBookmarks metod. Vi börjar med att initialisera bokmärkesmatrisen. Därefter frågar vi NSFileManager för URL-adressen till iCloud-behållaren vi använder för att lagra våra bokmärken. Släng inte av med det färgglada namnet på den här metoden. URLForUbiquityContainerIdentifier: accepterar ett argument, identifieraren för iCloud-behållaren vi vill få tillgång till. Genom att passera nil som argumentet, NSFileManager kommer automatiskt välja den första iCloud-behållaren som deklareras i vår applikations rättigheterfil. Observera att om du anger en iCloud-containeridentifierare måste du också ange lagidentifieraren. Det korrekta formatet är ..

 - (void) loadBookmarks if (! self.bookmarks) self.bookmarks = [[NSMutableArray alloc] init];  NSURL * baseURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier: nil]; om (baseURL) self.query = [[NSMetadataQuery alloc] init]; [self.query setSearchScopes: [NSArray arrayWithObject: NSMetadataQueryUbiquitousDocumentsScope]]; NSPredicate * predicate = [NSPredicate predicateWithFormat: @ "% K som '*'", NSMetadataItemFSNameKey]; [self.query setPredicate: predicate]; NSNotificationCenter * nc = [NSNotificationCenter defaultCenter]; [nc addObserver: self selector: @selector (queryDidFinish :) namn: NSMetadataQueryDidFinishGatheringNotification object: self.query]; [nc addObserver: self selector: @selector (queryDidUpdate :) namn: NSMetadataQueryDidUpdateNotification object: self.query]; [self.query startQuery]; 

Denna metod är inte bara för att ta reda på var vi kan lagra våra iCloud-dokument. Genom att framgångsrikt ringa den här metoden kommer operativsystemet att utvidga vår applikations sandlåda till att inkludera iCloud-behållarkatalogen som vi angav. Det betyder att vi måste ringa den här metoden innan vi kan börja läsa eller skriva data till den här katalogen. Om du skulle logga in den återlämnade URL-adressen till konsolen skulle du märka två oddities, (1) den återlämnade URL-adressen för iCloud-behållaren är en lokal URL (finns på enheten själv) och (2) den här lokala webbadressen lever inte i vår applikations sandlåda. Det sätt som ICloud fungerar på är att vi lagrar dokumenten vi vill lagra i iCloud i den här lokala katalogen NSFileManager ger oss. ICloud-demonen, som körs på vår enhet i bakgrunden, tar hand om synkroniseringsaspekten av dokumenten och det kommer att göra detta även om vår program inte körs.

Eftersom den lokala webbadressen lever utanför vår applikations sandlåda, måste vi anropa den här metoden innan vi läser eller skriver till den här katalogen. Genom att åberopa denna metod frågar vi operativsystemet om tillstånd att läsa och skriva till den här katalogen.

Låt oss fortsätta dissekera loadBookmarks metod. Vi verifierar att webbadressen vi kommer tillbaka från NSFileManager är inte lika med noll. Det senare innebär två viktiga saker, (1) vi har en plats som vi kan läsa från och skriva till och (2) iCloud är aktiverat på enheten. Den andra punkten är särskilt viktig eftersom inte alla enheter har iCloud aktiverat.

Om NSFileManager returnerade verkligen en giltig URL, vi initierar en instans av NSMetadataQuery och tilldela den till förekomstvariabeln som vi deklarerade tidigare. De NSMetadataQuery klassen tillåter oss att söka iCloud-behållaren för dokument. Efter initialisering av en instans av NSMetadataQuery, Vi specificerar omfattningen av vår sökning. I vårt fall söker vi i Dokument katalog i vår iCloud-behållare eftersom det är platsen där vi ska lagra bokmärkesdokumenten. Du kan förfina sökningen genom att ställa in ett sökprotikat. Om du är bekant med Core Data, så är det inte nytt för dig. Vår sökning blir enkel, vi söker efter alla dokument i dokumentkatalogen av vår iCloud-behållare, följaktligen asterisken i predikatet.

Innan vi börjar vår fråga är det viktigt att inse att vi inte borde förvänta oss ett omedelbart resultat tillbaka från vår fråga. Istället kommer vi att registrera vår visningscontroller som observatör för NSMetadataQueryDidUpdateNotification och NSMetadataQueryDidFinishGatheringNotification meddelanden med vår sökfråga som avsändare. Det innebär att vi kommer att bli underrättad när vår fråga har returnerat några resultat eller när resultaten har uppdaterats. Slutligen startar vi frågan.

Det är viktigt att vi hänvisar till frågeexemplet för att förhindra att det släpps. Det här är anledningen till att vår vyskontroller behåller en referens till frågan (som en instansvariabel) så länge som frågan körs.

Låt oss ta en titt på queryDidFinish: och queryDidUpdate: anmälnings återkopplingsmetoder för att se hur man hanterar resultaten av frågan. Båda metoderna överför meddelandets avsändarobjekt, NSMetadataQuery till exempel, för en bekväm metod, processQueryResults:. När vi tittar på den här metoden ser vi att vi först börjar med att inaktivera uppdateringar för frågan. Detta är viktigt eftersom resultaten av frågan kan få live uppdateringar när förändringar äger rum och vi måste förhindra det så länge vi behandlar sökfrågets resultat. Därefter tar vi bort alla objekt från vårt bokmärkes array och summerar resultaten från frågan. Varje objekt i resultatmatrisen är en förekomst av NSMetadataItem, som innehåller metadata som är associerade med varje bokmärkesdokument, inklusive filadressen vi behöver öppna dokumentet. Vi frågar varje metadataobjekt för filadressen och initierar respektive dokument.

Observera att initialisering av ett bokmärkesdokument inte betyder att vi har laddat det från disken. Kom ihåg att detta görs genom att skicka ett bokmärke till openWithCompletionHandler:. Om den öppna operationen lyckas och dokumentet är laddat lägger vi till det i vår uppsättning bokmärken och visar den i tabellvyn. Slutligen måste vi ta bort vår kontroller som observatör eftersom vi inte längre behöver ta emot meddelanden vid denna tidpunkt.

 - (void) queryDidFinish: (NSNotification *) notification NSMetadataQuery * query = [meddelandeobjekt]; // Stopp uppdateringar [fråga disableUpdates]; // Stop Query [query stopQuery]; // Rensa bokmärken [self.bookmarks removeAllObjects]; [query.results enumerateObjectsUsingBlock: ^ (id obj, NSUInteger idx, BOOL * stopp) NSURL * documentURL = [(NSMetadataItem *) obj valueForAttribute: NSMetadataItemURLKey]; BookmarkDocument * document = [[BookmarkDocument alloc] initWithFileURL: documentURL]; [dokument openWithCompletionHandler: ^ (BOOL-framgång) om (framgång) [self.bookmarks addObject: dokument]; [self.tableView reloadData]; ]; ]; [[NSNotificationCenter defaultCenter] removeObserver: self]; 

Steg 4B: Visa bokmärken i tabellvyn

Koden för att visa bokmärkenna i vår tabellvy behöver inte ändras mycket. I stället för att hämta rätt NSDictionary Exempel från datakällan, vi hämtar en instans av BookmarkDocument klass. Åtkomst till bokmärkes namn och webbadress måste också uppdateras.

 - (UITableViewCell *) tableView: (UITableView *) aTableView cellForRowAtIndexPath: (NSIndexPath *) indexPath static NSString * CellIdentifier = @ "Cell Identifier"; UITableViewCell * cell = [aTableView dequeueReusableCellWithIdentifier: CellIdentifier]; om (cell == nil) cell = [[UITableViewCell-allokering] initWithStyle: UITableViewCellStyleSubtitle reuseIdentifier: CellIdentifier];  // Hämta bokmärke BookmarkDocument * document = [self.bookmarks objectAtIndex: indexPath.row]; // Konfigurera Cell cell.textLabel.text = document.bookmark.name; cell.detailTextLabel.text = document.bookmark.url; returcell; 

Steg 4C: Spara en bokmärke

Gå över till spara: metod i AddBookmarkViewController. I stället för att skapa en NSDictionary och skickar den till vår huvudkontroller, skapar vi en ny Bokmärke exempel. Det är allt. Resten hanteras i saveBookmark: Metoden för vår huvudvyn kontroller. Glöm inte att lägga till en importdeklaration för Bokmärke klass.

 - (IBAction) spara: (id) avsändare Bookmark * bookmark = [[Bookmark alloc] initWithName: self.nameField.text andURL: self.urlField.text]; [self.viewController saveBookmark: bokmärke]; [self dismissViewControllerAnimated: YES completion: nil]; 

Att spara ett bokmärke till vår iCloud-behållare är nästan lika enkelt som att spara det i vår applikations sandlåda. Först frågar vi NSFileManager för URL-adressen till vår iCloud-behållare som vi gjorde tidigare. Baserat på den webbadressen konstruerar vi den rätta webbadressen för att spara bokmärkesdokumentet i Dokument iCloud-behållarens katalog. Namnet på vårt dokument kan vara vad vi vill att det ska vara så länge som namnet är unikt. Jag har valt att använda bokmärkes namn och en tidstämpel. Användaren kommer inte att se detta filnamn så namnet är inte så viktigt. Det som är viktigt är att det är unikt.

Vi har en bokmärkesinstans, men vi har ännu inte ett bokmärkesdokument. Vi skapar ett nytt bokmärkesdokument genom att initiera det med den webbadress vi just byggt. Därefter tilldelar vi vårt nya bokmärke till dokumentets bokmärkeegenskap. Slutligen lägger vi till dokumentet i bokmärkesmatrisen och laddar om tabellvyn.

Att spara dokumentet till iCloud-behållaren är enkelt. Vi initierar den spara operation jag pratade om tidigare genom att skicka vårt nya dokument meddelandet saveToURL: forSaveOperation: completionHandler:. Den andra parametern i den här metoden anger vilken typ av spara operation. I vårt fall passerar vi UIDocumentSaveForCreating, vilket innebär att skapa ett helt nytt bokmärkesdokument. Eftersom vi inte behöver göra något speciellt i vårt exempel loggar vi helt enkelt ett meddelande till konsolen när lagringsoperationen avslutas.

Du kanske har märkt att vår metoddeklaration ändrats något. Vi överlåter inte längre en förekomst av NSDictionary som det enda argumentet. I stället passerar vi en instans av Bokmärke klass. Se till att du uppdaterar headerfilen för att återspegla denna ändring. Du måste också lägga till en fowardklassdeklaration för att förhindra att kompilatorvarningar visas. Det här är uppgifter som du bör känna till nu.

 - (void) saveBookmark: (Bookmark *) bokmärke // Spara bokmärke NSURL * baseURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier: nil]; om (baseURL) NSURL * documentsURL = [baseURL URLByAppendingPathComponent: @ "Documents"]; NSURL * documentURL = [documentsURL URLByAppendingPathComponent: [NSString stringWithFormat: @ "Bokmärke _% @ -% f", bokmärke.namn, [[NSDate date] timeIntervalSince1970]]]; BookmarkDocument * document = [[BookmarkDocument alloc] initWithFileURL: documentURL]; document.bookmark = bookmark; // Lägg till bokmärke i bokmärken [self.bookmarks addObject: document]; // Uppdatera tabellvisning [self.tableView reloadData]; [dokument saveToURL: documentURL forSaveOperation: UIDocumentSaveForCreating completionHandler: ^ (BOOL-framgång) om (framgång) NSLog (@ "Spara lyckades.");  else NSLog (@ "Save failed."); ]; 

Steg 4D: Radera bokmärken

Den sista saknade pusselbiten är radering av bokmärken. Det här är väldigt enkelt jämfört med vad vi hittills gjort. Vi hämtar rätt bokmärkesdokument från datakällan och berättar NSFileManager för att radera det från iCloud-behållaren genom att skicka den korrekta webbadressen. Detta tar också bort dokumentet på iCloud. Det är så enkelt det är. Självklart uppdaterar vi också datakällan och tabellvisningen.

 - (void) tableView: (UITableView *) aTableView commitEditingStyle: (UITableViewCellEditingStyle) editingStyle forRowAtIndexPath: (NSIndexPath *) indexPath if (editingStyle == UITableViewCellEditingStyleDelete) // Hämta Document BookmarkDocument * document = [self.bookmarks objectAtIndex: indexPath.row] ; // Radera dokument NSError * error = nil; om ! [[NSFileManager defaultManager] removeItemAtURL: document.fileURL fel: & error]) NSLog (@ "Ett fel uppstod när försök att radera dokument. Fel% @ med användarinformation% @.", fel, error.userInfo);  // Uppdatera bokmärken [self.bookmarks removeObjectAtIndex: indexPath.row]; // Uppdatera tabellvy [aTableView deleteRowsAtIndexPaths: [NSArray arrayWithObject: indexPath] withRowAnimation: UITableViewRowAnimationFade]; 

Slutsats

I denna handledning har vi refactored vår ansökan för att använda iCloud Document Storage istället för iCloud Key Value Storage. Även om vi har refactored ganska lite kod var processen ganska enkel. Vi har nu en dokumentbaserad applikation som är mycket lämpligare för hantering av större mängder användardata. Grundkomponenterna är på plats och förlängningen av applikationen kräver liten ansträngning från vår sida. Observera att vår applikation fortfarande är en minimal implementering av en bokmärkeshanterare. Det finns till exempel inget alternativ att redigera bokmärken. Vi bör också göra mer felkontroll om vi vill göra vår ansökan till en robust och pålitlig applikation som är klar för utgivning.

Du kanske också har märkt att vi har ett antal metoder som vi inte behöver längre. Jag har tagit bort dessa metoder från det slutliga kodprovet och jag föreslår att du gör detsamma. Att ta bort föråldrad kod är också en del av refactoringprocessen och viktigt när du vill behålla din kod.

Nästa gång

I denna handledning tog vi en närmare titt på iCloud Document Storage såväl som UIDocument klass. I nästa handledning kommer vi att zooma in på UIManagedDocument, en diskret underklass av UIDocument utformad för att arbeta nära Core Data.