Kjernen data fra Scratch: Migrations

Core data fra Scratch: Migrations
26
Del
en
Del

Dette Cyber ​​mandag Envato Tuts + Kursene vil bli redusert til bare $ 3. Ikke gå glipp av
Dette innlegget er en del av en serie kalt kjernedata fra Scratch.Core data fra Scratch:. Mer NSFetchedResultsControllerCore data fra Scratch: subclassing NSManagedObject

I tidligere artikler i denne serien, har vi støtt en irriterende problem som vi må løse. Når vi endrer datamodell av en Core data program, blir vedvarende butikken uforenlig med datamodellen. Resultatet er en krasj på lanseringen, gjengi programmet ubrukelig, et alvorlig problem hvis dette skjer med en applikasjon i App Store.

Vårt program krasjer fordi vi påberope avbryte i persistentStoreCoordinator metoden hvis du legger til vedvarende butikk til den vedvarende butikken koordinator er mislykket. For å være klar, avbrytelsesfunksjonen fører til at programmet avsluttes umiddelbart
om. ([_ PersistentStoreCoordinator addPersistentStoreWithType: NSSQLiteStoreType konfigurasjon: nil URL: storeURL alternativer: null feil: & error]) {NSLog (@ "Uløste feil% @ ,% @ ", feil, [error Userinfo]); abortere ();}

Det er imidlertid ikke nødvendig å avslutte vår søknad, enn si krasjer den. Hvis kjernedata forteller datamodellen og vedvarende butikken er inkompatible, så er det opp til oss å løse dette.

I denne artikkelen vil vi diskutere to alternativer for å gjenopprette fra en slik situasjon, migrerer den vedvarende butikken og skape en ny vedvarende butikk som er kompatibel med den modifiserte datamodell.

1. Problemet

La meg først avklare problemet at vi prøver å løse. Last ned prøveprosjektet vi skapte i forrige artikkel, og kjøre den i iOS Simulator. Søknaden bør kjøre og fungere helt fint.

Åpne Done.xcdatamodeld Hotell og legge et attributt updatedAt av type Dato
til TSPItem enhet. Kjøre programmet en gang til, og legg merke til hvordan programmet krasjer så snart den er lansert. Heldigvis gir kjernedata oss en anelse om hva som gikk galt. Ta en titt på resultatet i Xcode konsoll
Error Domain = NSCocoaErrorDomain Code = 134100 "Handlingen kunne ikke fullføres (Cocoa error 134 100.)." Userinfo = 0x7c57e980 {metadata = {NSPersistenceFrameworkVersion = 513.; NSStoreModelVersionHashes = {TSPItem = < ce1c6693 4229b043 7cfe7324 4718b0c4 81c9af4d 12e71373 3288b42e 7de7ac62 >; }; NSStoreModelVersionHashesVersion = 3; NSStoreModelVersionIdentifiers = (""); NSStoreType = SQLite; NSStoreUUID = "C6212594-143E-4A26-9990-7FE5FD8B7336"; "_NSAutoVacuumLevel" = 2;}, fornuft = Modellen brukes til å åpne butikken er uforenlig med den som ble brukt for å lage den butikken}

I den siste linjen, forteller grunndata oss at datamodellen som ble brukt til å åpne den vedvarende butikken er uforenlig med datamodellen som det pleide å lage den vedvarende butikken. Vente. Hva?

Når vi lanserte programmet for første gang, skapte kjernedata en SQLite database basert på datamodellen. Men fordi vi endret datamodell ved å legge til et attributt til TSPItem enhet, updatedAt, Core data ikke lenger forstår hvordan det bør lagre TSPItem poster i SQLite database. Med andre ord, er den modifiserte datamodellen ikke lenger er forenlig med den vedvarende butikken det opprettet tidligere.

2. Løsningen

Heldigvis for oss, har noen smarte ingeniørene hos Apple laget en løsning for å trygt endre en datamodell uten å kjøre inn inkompatibilitet problemer. For å løse problemet vi står overfor, må vi finne en måte å fortelle kjernedata hvordan én versjon av datamodellen er knyttet til en annen versjon. Det er riktig, versjonsdatamodellen er en del av løsningen.

Med denne informasjonen, Core Data kan forstå hvordan den vedvarende butikken må oppdateres for å være kompatibel med den modifiserte datamodell, det vil si den nye versjonen av datamodellen. Med andre ord, må vi levere grunndata nødvendig informasjon for å migrere vedvarende butikken fra én versjon av datamodell til en annen.

3. Migrations

Det finnes to typer av vandringer, lette og tunge vandringer. Ordene lette Hotell og tung
er ganske beskrivende, men det er viktig at du forstår hvordan kjernedata håndterer hver type migrasjon.

Lette Migrations

Lette vandringer krever svært lite arbeid fra din side, utvikleren. Jeg anbefaler sterkt at du velger en lett migrasjon over en tung migrasjon når du kan. Kostnaden for en lett migrasjon er vesentlig lavere enn for en tung migrasjon.

Selvfølgelig, er baksiden av lette vandringer at de er mindre kraftig enn tunge vandringer. De endringer du kan gjøre til en datamodell med lette vandringer er begrenset. For eksempel kan en lett migrasjon du legge til eller endre navn på attributter og enheter, men du kan ikke endre type for attributtet eller relasjonen mellom eksisterende enheter.

Lette migreringer er ideelle for å utvide datamodellen, legger attributter og enheter. Hvis du har tenkt å modifisere relasjoner eller endre attributt typer, så du er i for en vill tur med tunge vandringer.

Store Migreringer

Store overføringer er litt vanskeligere. La meg omformulere spørsmålet. Tunge vandringer er en smerte i nakken, og du bør prøve å unngå dem hvis mulig. Tunge migreringer er kraftige, men at makten kommer til en kostnad. Tungvandringene krever mye arbeid og testing for å sikre at overgangen blir fullført og, enda viktigere, uten tap av data.

Vi går inn i en verden av tunge vandringer hvis vi gjør endringer som kjernedata ikke automatisk kan antyde for oss ved å sammenligne versjoner av datamodellen. Kjerne Data vil da trenge en kartleggingsmodell for å forstå hvordan versjonene av datamodellen forholder seg til hverandre. Fordi tunge vandringer er et komplekst tema, vil vi ikke dekke det i denne serien.

4. Versjons

Hvis du har jobbet med Ruby on Rails, så migrasjoner vil gjøre mye fornuftig for deg. Ideen er enkel, men kraftig. Kjernen data gjør oss til versjon datamodellen, og dette gjør at vi kan trygt endre datamodellen. Kjernen data inspiserer versjondatamodell for å forstå hvordan den vedvarende butikken er relatert til datamodellen. Ved å se på versjondatamodellen, vet det også hvis den vedvarende butikken må overføres før den kan brukes sammen med den gjeldende versjonen av datamodellen.

Versjons og vandringer går hånd i hånd. Hvis du ønsker å forstå hvordan vandringer fungere, må du først forstå hvordan versjon Core data datamodell. La oss se på to-do program vi opprettet i forrige artikkel. Som vi så tidligere, og legger til et attributt, updatedAt, til TSPItem foretakets resultater i den vedvarende butikken å være uforenlig med den modifiserte datamodell. Vi nå forstår årsaken til dette.

La oss starte med blanke ark ved å åpne Done.xcdatamodeld Hotell og fjerne updatedAt attributtet fra TSPItem enhet. Det er på tide å lage en ny versjon av datamodellen.

Med datamodellen valgt, velger du Legg Model versjon ...
fra Editor
menyen. Xcode vil be deg om å gi navn til den nye datamodellen versjon og, enda viktigere, på hvilken versjon den nye versjonen skal være basert. For å sikre kjernedata kan migrere den vedvarende butikken for oss, er det viktig at du velger den forrige versjonen av datamodellen. I dette eksempelet har vi bare ett valg.

Resultatet av denne handlingen er at vi nå kan se tre data modellfiler i Prosjekt Navigator
. Det er ett toppnivå datamodell med en .xcdatamodeld
forlengelse og to barn med en .xcdatamodel
forlengelse.

Du kan se .xcdatamodeld
fil som en pakke for versjonene av datamodellen, med hver versjon representert med en .xcdatamodel
fil. Du kan kontrollere dette ved å Vis i Finder
høyreklikke på .xcdatamodeld
fil og velge. Dette vil ta deg til datamodellen i Xcode prosjekt. Hvis du høyreklikker filen og velg Vis innholdet i pakken
, skal du se de to versjonene av datamodellen, Done.xcdatamodel Hotell og Ferdig 2
.xcdatamodel
.

Har du lagt merke til i Prosjekt Navigator
at en av versjonene har en grønn hake? Denne merkingen indikerer hva dagens modell versjonen er, Done.xcdatamodel
i dette eksemplet. Med andre ord, selv om vi har laget en ny versjon av datamodellen, det er ikke tatt i bruk av vår søknad ennå. Før vi endrer dette, skjønt, må vi fortelle kjernedata hva den skal gjøre med versjondatamodell.

Vi må fortelle kjernedata hvordan du overfører den vedvarende butikken for datamodellen. Vi gjør dette i persistentStoreCoordinator metoden i TSPAppDelegate.m
. I persistentStoreCoordinator metoden, skaper vi den vedvarende butikken koordinator og vi legger en vedvarende butikken til det ved å påberope addPersistentStoreWithType: konfigurasjon: URL: alternativer: error :. Dette er ikke noe nytt.

Den fjerde parameter av denne metoden, en ordbok av alternativer, er for tiden null. Denne ordlisten alternativer inneholder instruksjoner for kjernedata. Det gir oss muligheten til å fortelle den vedvarende butikken koordinator hvordan det skal migrere vedvarende butikk som vi legger til det.

Ta en titt på den oppdaterte gjennomføring av persistentStoreCoordinator der passerer vi en ordliste over alternativer med to viktige -verdi parene Anmeldelser - (NSPersistentStoreCoordinator *) persistentStoreCoordinator {if (_persistentStoreCoordinator) {return _persistentStoreCoordinator.; } NSURL * applicationDocumentsDirectory = [[[NSFileManagers defaultManager] URLsForDirectory: NSDocumentDirectory inDomains: NSUserDomainMask] lastObject]; NSURL * storeURL = [applicationDocumentsDirectory URLByAppendingPathComponent: @ "Done.sqlite"]; NSError * error = null; _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [selvtillit managedObjectModel]]; NSDictionary * options = @ {NSMigratePersistentStoresAutomaticallyOption: @ (JA), NSInferMappingModelAutomaticallyOption: @ (JA)}; if ([_ persistentStoreCoordinator addPersistentStoreWithType: NSSQLiteStoreType konfigurasjon: nil URL: storeURL alternativer: alternativer error: & error]) {NSLog (@ "Uløste feil% @,% @", feil, [error Userinfo]); abortere (); } Returnere _persistentStoreCoordinator;}

Den første nøkkelen, NSMigratePersistentStoresAutomaticallyOption, forteller kjernedata som vi vil at det skal forsøke å migrere vedvarende butikken for oss. Den andre nøkkelen, NSInferMappingModelAutomaticallyOption, instruerer kjernedata for å antyde kartlegging modell for migrasjon. Dette er akkurat det vi ønsker. Det skal fungere uten problemer så lenge vi har å gjøre med en lett migrasjon.

Med denne endringen, er vi klare til å overføre datamodellen til den nye versjonen har vi laget en liten stund siden. Start med å velge den nye versjonen, Ferdig 2.xcdatamodel
, og legge til en ny attributt updatedAt av type Dato
til TSPItem foretaket.

Vi trenger også å markere ny datamodell versjon som versjonen til bruk av grunndata. Velg
Done.xcdatamodeld
i Prosjekt Navigator Hotell og åpne File Inspektør
til høyre. I avsnittet Model versjon
, sette Gjeldende
til Ferdig 2
.

Prosjekt Navigator
, Ferdig 2.xcdatamodel
skal nå ha en grønn hake i stedet for Done.xcdatamodel
.

Med denne endringen, kan du trygt bygge og kjøre programmet. Hvis du har fulgt fremgangsmåten ovenfor, bør kjernedata automatisk migrere vedvarende butikken for deg ved å dedusere kartlegging modell basert på versjondatamodell.

Legg merke til at det er noen begrensninger du bør være klar over. Hvis du kjører inn i en krasj, så du har gjort noe galt. For eksempel, hvis du har satt den datamodell versjonen til Ferdig 2.xcdatamodel
, kjøre programmet, og deretter gjøre endringer i Ferdig 2.xcdatamodel
, så vil du nødvendigvis kjøre inn i en krasj på grunn av den vedvarende butikken å være uforenlig med datamodellen. Lette migreringer er relativt kraftig og de er enkle å implementere, men det betyr ikke at du kan endre datamodellen til enhver tid.

Data laget av en programvare-prosjekt krever omsorg, oppmerksomhet og forberedelse. Migrations er stor, men de bør brukes med måte. Krasjer er ikke noe problem under utvikling, men de er katastrofale i produksjon. I neste avsnitt tar vi en nærmere titt på hva dette betyr, og hvordan du kan forhindre krasj på grunn av en problematisk migrasjon.

5. Unngå krasjer

Jeg har aldri kommet i en situasjon som ikke er garantert å kalle abort i produksjon, og det smerter meg når jeg besøker et prosjekt der Apples standard implementering for å sette opp grunndata stabelen brukes, hvor abort er kalt når du legger til et vedvarende butikken er mislykket.

Unngå abort er ikke så vanskelig, men det krever noen få linjer med kode og informere brukeren om hva som gikk galt i tilfelle noe går galt. Utviklere er bare mennesker og vi alle gjør feil

Trinn 1:. Bli kvitt abort

Start med å åpne TSPAppDelegate.m Hotell og fjerne linjen som vi kaller abort . Det er det første skrittet til en lykkelig bruker
om. ([_ PersistentStoreCoordinator addPersistentStoreWithType: NSSQLiteStoreType konfigurasjon: nil URL: storeURL Alternativer: alternativer error: & error]) {NSLog (@ "Uløste feil% @,% @", feil, [error Userinfo]);}
Trinn 2: Flytte Inkompatibel butikken

Hvis grunndata oppdager at den vedvarende butikken er uforenlig med datamodellen, må vi først flytte uforenlig butikken til et trygt sted . Vi gjør dette for å sikre at brukerens data er ikke tapt. Selv om datamodellen er uforenlig med den vedvarende butikken, kan du være i stand til å gjenopprette data danne det. Ta en titt på den oppdaterte gjennomføringen av persistentStoreCoordinator metoden i TSPAppDelegate.m
- (NSPersistentStoreCoordinator *) persistentStoreCoordinator {if (_persistentStoreCoordinator) {return _persistentStoreCoordinator.; } NSURL * storeURL = [[selvtillit applicationStoresDirectory] URLByAppendingPathComponent: @ "Store.sqlite"]; NSError * error = null; _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [selvtillit managedObjectModel]]; if ([_ persistentStoreCoordinator addPersistentStoreWithType: NSSQLiteStoreType konfigurasjon: nil URL: storeURL alternativer: null feil: & error]) {NSFileManagers * fm = [NSFileManagers defaultManager]; //Flytt Inkompatibel Butikk if ([fm fileExistsAtPath: [storeURL bane]]) {NSURL * corruptURL = [[selvtillit applicationIncompatibleStoresDirectory] URLByAppendingPathComponent: [selvtillit nameForIncompatibleStore]]; //Flytt Corrupt Butikk NSError * errorMoveStore = null; [fm moveItemAtURL: storeURL toURL: corruptURL error: & errorMoveStore]; if (errorMoveStore) {NSLog (@ "Kan ikke flytte korrupt butikken."); }}} Returnere _persistentStoreCoordinator;}

Merk at jeg har endret verdien av storeURL, plasseringen av den vedvarende butikken. Den peker til en katalog i dokumenter katalog i programmets sandkasse. Gjennomføringen av applicationStoresDirectory, en hjelper metode, er grei som du kan se nedenfor Anmeldelser - (NSURL *) applicationStoresDirectory {NSFileManagers * fm = [NSFileManagers defaultManager].; NSURL * applicationApplicationSupportDirectory = [[fm URLsForDirectory: NSApplicationSupportDirectory inDomains: NSUserDomainMask] lastObject]; NSURL * URL = [applicationApplicationSupportDirectory URLByAppendingPathComponent: @ "Stores"]; if ([fm fileExistsAtPath: [URL bane]]) {NSError * error = null; [FM createDirectoryAtURL: URL withIntermediateDirectories: JA attributter: null feil: & error]; if (feil) {NSLog (@ "Kan ikke lage katalog for datalagre."); returnere null; }} Avkastning URL;}

Hvis vedvarende butikken koordinator er i stand til å legge den eksisterende vedvarende butikken på storeURL, flytter vi den vedvarende butikken til en egen katalog. Legg merke til at vi gjør bruk av to hjelpemetoder, applicationIncompatibleStoresDirectory og nameForIncompatibleStore. Gjennomføringen av applicationIncompatibleStoresDirectory er ganske enkelt som du kan se nedenfor
- (NSURL *) applicationIncompatibleStoresDirectory {NSFileManagers * fm = [NSFileManagers defaultManager].; NSURL * URL = [[selvtillit applicationStoresDirectory] URLByAppendingPathComponent: @ "Inkompatibel"]; if ([fm fileExistsAtPath: [URL bane]]) {NSError * error = null; [FM createDirectoryAtURL: URL withIntermediateDirectories: JA attributter: null feil: & error]; if (feil) {NSLog (@ "Kan ikke lage katalog for korrupte datalagre."); returnere null; }} Avkastning URL;}

I nameForIncompatibleStore, vi genererer et navn for den inkompatible butikken basert på gjeldende dato og tid for å unngå å navngi kollisjoner Anmeldelser - (NSString *) nameForIncompatibleStore {//Initial Dato Atter NSDateFormatter * dateFormatter. = [[NSDateFormatter Alloc] init]; //Konfigurer Dato Atter [dateFormatter setFormatterBehavior: NSDateFormatterBehavior10_4]; [dateFormatter setDateFormat: @ "åååå-MM-dd-HH-mm-ss"]; tilbake [NSString stringWithFormat: @ "% @ SQLite.", [dateFormatter stringFromDate: [NSDate dato]]];}
Trinn 3: Lage en ny Vedvarende Butikk

Det er på tide å lage en ny vedvarende butikk for å fullføre oppsettet av kjernedata stabelen. De neste par linjer bør se veldig kjent nå
NSError * errorAddingStore = null, hvis. (! [_ PersistentStoreCoordinator addPersistentStoreWithType: NSSQLiteStoreType konfigurasjon: nil URL: storeURL alternativer: null feil: & errorAddingStore]) {NSLog (@ "Unable å skape vedvarende butikken etter utvinning% @,% @ ", errorAddingStore, errorAddingStore.localizedDescription);.}

Hvis kjernedata ikke er i stand til å skape en ny vedvarende butikken, så er det mer alvorlige problemer som ikke er relatert til data Modellen er uforenlig med den vedvarende butikken. Hvis du kjører inn i denne saken, og dobbeltsjekke verdien av storeURL

Trinn 4:. Varsle bruker:

Dette trinnet er trolig det viktigste når det gjelder å skape en brukervennlig søknad. Å miste brukerens data er en ting, men å late som om ingenting har skjedd er ikke hyggelig. Hvordan ville du føle hvis et flyselskap mistet bagasjen din, late som om ingenting har skjedd
//Show Alert ViewNSString * title = @ "Warning"; NSString * applicationName = [[NSBundle mainBundle] objectForInfoDictionaryKey: @ "CFBundleDisplayName"].; NSString * melding = [NSString stringWithFormat: @ "En alvorlig programfeil oppstod under% @ prøvde å lese data Vennligst kontakt support for å få hjelp..", applicationName]; UIAlertView * alertView = [[UIAlertView alloc] initWithTitle: title melding: melding delegat: nil cancelButtonTitle: @ "OK" otherButtonTitles: nil] [alertView vis];

Vi viser et varsel utsikt til brukeren, men det er en god idé å ta det noen skritt videre. For eksempel kan du oppfordre dem til å ta kontakt med support og du kan også implementere en funksjon som lar dem sende deg den korrupte butikken. Sistnevnte er svært nyttig for feilsøking av problemet

Trinn 5:. Testing

Vedvarende data er en viktig del av de fleste programmer. Det er derfor viktig å riktig teste det vi har implementert i denne artikkelen. For å teste vår utvinningsstrategi, kjører programmet i iOS Simulator og dobbeltsjekke at den vedvarende butikken ble opprettet i Application Support katalog, i Butikker
katalogen.

Legg til en ny attributt til TSPItem enhet i Ferdig 2.xcdatamodel Hotell og kjøre programmet en gang til. Fordi den eksisterende vedvarende butikken er nå uforenlig med datamodellen, er uforenlig vedvarende butikken flyttet til Ikke kompatibel
underkatalogen og en ny vedvarende butikken er opprettet. Du bør også se et varsel utsikt, informere brukeren om problemet.

Konklusjon

Migrations er et viktig tema hvis du har tenkt å gjøre utstrakt bruk av grunndata. Migrations la deg trygt modifisere datamodell programmets og i tilfelle av lette vandringer, uten mye overhead.

I neste artikkel, fokuserer vi på subclassing NSManagedObject. Hvis en Core data prosjektet har noen form for kompleksitet, er da subclassing NSManagedObject veien å gå.