Kjernen data og Swift: Managed Objects og Fetch Requests

Core data og Swift: Managed Objects og Fetch Forespørsler
17
Del
7
Del

Dette Cyber ​​Monday Envato Tuts + kurs vil bli redusert til bare $ 3. Ikke gå glipp av
Dette innlegget er en del av en serie kalt kjernedata og Swift.Core Data- og Swift. Data ModelCore data og Swift: Relasjoner og Mer Henter

Med alt om Cora data datamodeller fortsatt friskt i tankene dine, er det på tide å begynne å jobbe med kjernedata. I denne artikkelen møter vi NSManagedObject, klassen du vil samhandle med de fleste når du arbeider med kjernedata. Du vil lære å opprette, lese, oppdatere og slette poster.

Du vil også få vite et par andre Kjerne-dataklasser, som NSFetchRequest og NSEntityDescription. La meg starte med å introdusere deg til NSManagedObject, din nye bestevenn.

Forutsetninger

Det jeg dekke i denne serien på kjernedata er aktuelt for iOS 7+ og OS X 10.10+, men Fokus vil være på iOS. I denne serien vil jeg jobbe med Xcode 7.1 og Swift 2.1. Hvis du foretrekker Objective-C, så jeg anbefaler å lese min tidligere serie på grunndata rammeverket.

1. Managed Objects

Forekomster av NSManagedObject representerer en rekord i Core-Datas backing butikken. Husk, det spiller ingen rolle hva som backing butikken ser ut. Men for å dra tilbake til den databasen analogi, inneholder en NSManagedObject eksempel informasjon om en rad i en databasetabell.

Grunnen kjernedata bruker NSManagedObject istedenfor NSObject som sin base klasse for modellering postene vil være mer fornuftig litt seinere. Før vi begynner å arbeide med NSManagedObject, trenger vi å vite noen ting om denne klassen.

NSEntityDescription

Hver NSManagedObject eksempel er assosiert med en forekomst av NSEntityDescription. Foretaket beskrivelse inneholder informasjon om den klarte objekt, for eksempel enhet
av forvaltet objekt samt sin attributter Hotell og relasjoner
.

NSManagedObjectContext

En administrert objekt er også knyttet til en forekomst av NSManagedObjectContext. Den klarte objektet sammenheng som en administrert objekt tilhører, overvåker klarte gjenstand for endringer.

2. Opprette en Record

Med dette i tankene, skaper en administrert objekt er ganske grei. Å sørge for en administrert objekt er riktig konfigurert, er det anbefalt å bruke den utpekte initializer for å skape nye NSManagedObject tilfeller. La oss se hvordan dette fungerer ved å opprette en ny person objekt.

Åpne prosjektet fra forrige artikkel eller klone depotet fra GitHub. Fordi vi ikke skal bygge et funksjonelt program i denne artikkelen, vil vi gjøre det meste av vårt arbeid i søknaden delegat klassen, AppDelegate. Åpne AppDelegate.swift Hotell og oppdatere gjennomføringen av programmet (_: didFinishLaunchingWithOptions :) som vist nedenfor
func søknad (søknad: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject].?) - ≫ Bool {//Opprett Managed Object la entityDescription = NSEntityDescription.entityForName ("Person", inManagedObjectContext: self.managedObjectContext) la newPerson = NSManagedObject (enhet: entityDescription !, insertIntoManagedObjectContext: self.managedObjectContext) return true}

Det første vi gjøre, er å skape en forekomst av NSEntityDescription klassen ved å påberope entityForName (_: inManagedObjectContext :). Vi passerer navnet av foretaket vi ønsker å skape en administrert objekt for "Person", og en NSManagedObjectContext eksempel.

Hvorfor trenger vi å passere i en NSManagedObjectContext objekt? Vi spesifiserer navnet som vi ønsker å skape en administrert objekt for, men vi trenger også å fortelle kjernedata hvor den kan finne datamodellen for foretaket. Husk at en administrert objekt sammenheng er knyttet til et vedvarende butikken koordinator og en vedvarende butikken koordinator holder en referanse til en datamodell. Når vi passerer i en administrert objekt sammenheng, spør kjernedata sin vedvarende butikken koordinator for sin datamodell for å finne enheten vi leter etter.

I det andre trinnet, påberope vi utpekt initializer av NSManagedObject klasse , init (enhet: insertIntoManagedObjectContext :). Vi passerer i foretaket beskrivelse og en NSManagedObjectContext eksempel. Vente? Hvorfor trenger vi å passere i en annen NSManagedObjectContext eksempel? Husk hva jeg skrev tidligere. En administrert objekt er involvert i et aksjeselskap beskrivelse og Selge den lever i en administrert objekt sammenheng, og det er derfor vi fortelle kjernedata som klarte objekt sammenheng den nye greid objekt bør knyttes til.

Dette er ikke altfor komplisert. Er det? Vi har nå opprettet en ny person objekt. Hvordan endrer vi attributtene eller definere et forhold? Dette gjøres ved å utnytte nøkkelverdien koding. Å endre det første navnet på den nye personen objektet vi nettopp opprettet vi gjøre følgende:
//Konfigurer New PersonnewPerson.setValue ("Bart", Forkey: "første") newPerson.setValue ("Jacobs", Forkey: " siste ")

Hvis du er kjent med nøkkelverdien koding, så dette bør se veldig kjent. Fordi NSManagedObject klasse i samsvar med NSKeyValueCoding protokollen, stiller vi et attributt ved å påberope SetValue (_: Forkey :). Det er så enkelt.

En ulempe med denne tilnærmingen er at du enkelt kan introdusere bugs ved feilstaving et attributt eller relasjon navn. Også attributtnavnene er ikke autocompleted av Xcode som for eksempel egenskapsnavnene. Dette problemet er lett å løse, men det er noe vi vil ta en titt på litt senere i denne serien.

Før vi fortsetter vår utforskning av NSManagedObject, la oss sette en alder egenskap av newPerson til 44.
newPerson.setValue (44, Forkey: "age")
3. Lagre en Record

Selv om vi nå har en ny person eksempel har grunndata lagres ikke personen til sin backing butikken ennå. I øyeblikket, den klarte objektet vi skapte bare bor i forvaltet objekt i hvilken sammenheng det ble satt inn. Å redde personen objektet til backing butikken, trenger vi å lagre endringene av den administrerte objektet sammenheng ved å ringe redning () på den.

lagre () metoden er en kaste metode og returnerer en boolsk å indikere resultatet av lagringen. Ta en titt på følgende kodeblokken for avklaring.
Do {prøve newPerson.managedObjectContext? .Lagre ()} Catch {print (feil)}

Bygg og kjøre programmet for å se om alt fungerer som forventet. Har du også kjøre inn i en krasj? Hva gjorde i konsollet for deg? Gjorde det ligne utgangs under
kjernedata [8560: 265446] *** Avslutte app grunn uoppfanget unntak 'NSInvalidArgumentException', grunn: "Uakseptabelt type verdi for attributtet:? Property =" første "; ønsket type = NSDate; gitt type = Swift._NSContiguousString; value = Bart. '*** Første kast kallstakken :( 0 Corefoundation 0x000000010c3f1f45 __exceptionPreprocess + 165 1 libobjc.A.dylib 0x000000010e118deb objc_exception_throw + 48 2 CoreData 0x000000010bf8d840 _PFManagedObject_coerceValueForKeyWithDescription + 2864 3 CoreData 0x000000010bf660d1 _sharedIMPL_setvfk_core + 177 4 kjernedata 0x000000010be82200 _TFC9Core_Data11AppDelegate11applicationfS0_FTCSo13UIApplication29didFinishLaunchingWithOptionsGSqGVSs10DictionaryCSo8NSObjectPSs9AnyObject____Sb + 624 5 kjernedata 0x000000010be82683 _TToFC9Core_Data11AppDelegate11applicationfS0_FTCSo13UIApplication29didFinishLaunchingWithOptionsGSqGVSs10DictionaryCSo8NSObjectPSs9AnyObject____Sb + 179 6 UIKit 0x000000010cc07034 - [UIApplication _handleDelegateCallbacksWithOptions: isSuspended: restoreState:] + 272 7 UIKit 0x000000010cc081da - [UIApplication _callInitializationDelegatesForMainScene: transitionContext:] + 3415 8 UIKit 0x000000010cc0ead3 - [UIApplication _runWithMainScene: transitionContext: ferdigstillelse:] + 1750 9 UIKit 0x000000010cc0bcb3 - [ ,,,0],UIApplication workspaceDidEndTransaction:] + 188 10 FrontBoardServices 0x0000000110000784 - [FBSSerialQueue _performNext] + 192 11 FrontBoardServices 0x0000000110000af2 - [FBSSerialQueue _performNextFromRunLoopSource] + 45 12 Corefoundation 0x000000010c31e011 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17 13 Corefoundation 0x000000010c313f3c __CFRunLoopDoSources0 + 556 14 Corefoundation 0x000000010c3133f3 __CFRunLoopRun + 867 15 Corefoundation 0x000000010c312e08 CFRunLoopRunSpecific + 488 16 UIKit 0x000000010cc0b605 - [UIApplication _run] + 402 17 UIKit 0x000000010cc1041d UIApplicationMain + 171 18 kjernedata 0x000000010be8377d hoved + 109 19 libdyld.dylib 0x000000010ec3092d starte + en 20 ??? 0x0000000000000001 0x0 + 1) libc ++ abi.dylib å avslutte med uoppfanget unntak av typen NSException

Xcode forteller oss at det forventes en NSDate eksempel for første attributt, men vi passerte i en String. Hvis du åpner Core datamodellen vi skapte i den forrige artikkelen, vil du se at den type første attributt er faktisk Dato
. Endre den til String Hotell og kjøre programmet en gang til.

En annen krasjer? Selv om dette er en mer avansert emne, er det viktig å forstå hva som skjer.

Data Model Kompatibilitet

Utgangen i Xcode konsoll bør ligne utgangs vist nedenfor. Legg merke til at feilen er forskjellig fra den forrige. Xcode forteller oss at modell for å åpne butikken er uforenlig med den som ble brukt for å lage den butikken
. Hvordan skjedde dette
kjernedata [8879: 267986] CoreData: error: -addPersistentStoreWithType: SQLite konfigurasjon: (null) URL:file:///Users/Bart/Library/Developer/CoreSimulator/Devices/A263775B-4D73-48C8-BD79-825E0BED5128/data/Containers/Data/Application/D7298848-FC36-46EF-8C35-F890F2DB0C89/Documents/SingleViewCoreData.sqlite alternativer: (null) ... returnert feil Feil Domain = NSCocoaErrorDomain Code = 134100 "(null)" Userinfo = {metadata = {NSPersistenceFrameworkVersion = 640; NSStoreModelVersionHashes = {Adresse = < 268460b1 0507da45 f37f8fb5 b17628a9 a56beb9c 8666f029 4276074d 11160d13 >; Person = < c9bed257 c4bca383 38cd682a 227f38a8 c1a5bb27 fb02932c 42c62714 47463637 >; }; NSStoreModelVersionHashesVersion = 3; NSStoreModelVersionIdentifiers = (""); NSStoreType = SQLite; NSStoreUUID = "818D6962-8576-4F35-A334-A1A470561950"; "_NSAutoVacuumLevel" = 2;}, fornuft = Modellen brukes til å åpne butikken er uforenlig med den som ble brukt for å lage den butikken} med userinfo ordboken {metadata = {NSPersistenceFrameworkVersion = 640; NSStoreModelVersionHashes = {Adresse = < 268460b1 0507da45 f37f8fb5 b17628a9 a56beb9c 8666f029 4276074d 11160d13 >; Person = < c9bed257 c4bca383 38cd682a 227f38a8 c1a5bb27 fb02932c 42c62714 47463637 >; }; NSStoreModelVersionHashesVersion = 3; NSStoreModelVersionIdentifiers = (""); NSStoreType = SQLite; NSStoreUUID = "818D6962-8576-4F35-A334-A1A470561950"; "_NSAutoVacuumLevel" = 2; }; Grunnen = "Modellen brukes til å åpne butikken er uforenlig med den som ble brukt for å lage den store";} kjernedata [8879: 267986] Uløst error Error Domain = YOUR_ERROR_DOMAIN Code = 9999 "Kunne ikke initial lagrede data programmets" Userinfo = {NSLocalizedDescription = Kunne ikke initial lagrede data programmets, NSLocalizedFailureReason = Det oppstod en feil å opprette eller laste lagrede data programmets., NSUnderlyingError = 0x7fde6d9acc00 {Feil Domain = NSCocoaErrorDomain Code = 134100 "(null)" Userinfo = {metadata = {NSPersistenceFrameworkVersion = 640; NSStoreModelVersionHashes = {Adresse = < 268460b1 0507da45 f37f8fb5 b17628a9 a56beb9c 8666f029 4276074d 11160d13 >; Person = < c9bed257 c4bca383 38cd682a 227f38a8 c1a5bb27 fb02932c 42c62714 47463637 >; }; NSStoreModelVersionHashesVersion = 3; NSStoreModelVersionIdentifiers = (""); NSStoreType = SQLite; NSStoreUUID = "818D6962-8576-4F35-A334-A1A470561950"; "_NSAutoVacuumLevel" = 2;}, fornuft = Modellen brukes til å åpne butikken er uforenlig med den som ble brukt for å lage den butikken

Når vi først lanserte programmet en liten stund siden, Core data inspisert datamodellen, og basert på denne modellen, skapte en butikk for oss, en SQLite database i dette tilfellet. Kjernen data er flink skjønt. Det gjør at konstruksjonen av støtte butikken og som av datamodellen er kompatible. Dette er viktig å sørge for at vi får tilbake fra underlags butikken hva vi forventer og hva vi legger det i første omgang.

I løpet av den første ulykken, la vi merke til at vår datamodell inneholdt en feil og vi endret typen av den første attributtet fra Dato
til String
. Med andre ord, vi endret datamodellen selv om kjernedata allerede hadde opprettet kompet butikken for oss basert på feil datamodell.

Etter oppdatering av datamodellen, lanserte vi programmet på nytt, og kjørte inn i den andre brak. En av de tingene kjernedata gjør når det skaper Core data stabelen er å sørge for datamodellen og kompet butikk-hvis det finnes-er kompatible. Det var ikke tilfelle i vårt eksempel dermed ulykken.

Hvordan kan vi løse dette? Den enkle løsningen er å fjerne programmet fra enheten eller fra simulator, og starte programmet på nytt. Men dette er noe du ikke kan gjøre hvis du allerede har en applikasjon i App Store som folk bruker. I så fall må du bruke vandringer, som er noe vi vil diskutere i en senere artikkel.

Fordi vi ikke har millioner av brukere ved hjelp av vår søknad, kan vi trygt fjerne programmet fra vår test enheten og kjøre den på nytt. Hvis alt gikk bra, er den nye personen nå trygt lagret i butikken, SQLite database kjernedata skapt for oss.

Kontroll av Backing butikken

Du kan bekrefte at lagringsoperasjonen jobbet ved ta en titt på innsiden av SQLite database. Hvis du kjørte programmet i simulatoren, og deretter navigere til /Users/<USER>/Library/Developer/CoreSimulator/Devices/<DEVICE_ID>/data/Containers/Data/Application/<APPLICATION_ID>/Documents/SingleViewCoreData
.sqlite
. Fordi plasseringen av søknaden data endres med hver utgivelse av Xcode, er banen ovenfor gjelder bare for Xcode 7.

Åpne SQLite database og inspisere tabellen navnet ZPERSON
. Tabellen skal ha én post, den vi satt inn et minutt siden.

Du bør ha to ting i tankene. Først, det er ikke nødvendig å forstå databasestrukturen. Kjernen data forvalter backing butikken for oss, og vi trenger ikke å forstå dens struktur for å jobbe med kjernedata. For det andre, aldri få tilgang til backing butikken direkte. Kjernen data er ansvarlig for backing butikken og vi må respektere at hvis vi ønsker kjernedata for å gjøre jobben sin godt. Hvis vi begynner å samhandle med SQLite database-eller hvilken som helst annen butikk type det er ingen garanti Core-Data vil fortsette å fungere skikkelig. Kort sagt, er kjernedata ansvarlig for butikken så la det være.

4. Henter Records

Selv om vi tar en nærmere titt på NSFetchRequest i neste artikkel, trenger vi NSFetchRequest klassen til å be Kjerne Data for informasjon fra objektet grafen den klarer. La oss se hvordan vi kan hente posten vi satt inn tidligere bruker NSFetchRequest
//Initial Fetch Requestlet fetchRequest = NSFetchRequest () //Lag Entity Descriptionlet entityDescription = NSEntityDescription.entityForName ("Person", inManagedObjectContext: self.managedObjectContext). //Konfigurer Fetch RequestfetchRequest.entity = entityDescriptiondo {la resultat = prøve self.managedObjectContext.executeFetchRequest (fetchRequest) print (resultat)} catch {la fetchError = feil som NSError print (fetchError)}

Etter initialisering hente forespørsel, skaper vi en NSEntityDescription protestere og tilordne den til foretaket eiendom hente forespørsel. Som du kan se, bruker vi NSEntityDescription klassen å fortelle kjernedata hva foretaket vi er interessert i.

Henter data håndteres av NSManagedObjectContext klassen. Vi påberope executeFetchRequest (_ :), passerer i hente forespørsel. Fordi executeFetchRequest (_ :) er en kaste metode, pakk vi metodekallet i en gjør-fangst uttalelse.

Metoden returnerer en matrise av resultater hvis hente forespørselen er vellykket. Merk at kjernedata alltid returnerer en matrise hvis hente forespørselen er vellykket, selv om vi forventer ett resultat eller hvis kjernedata ikke finne noen samsvarende poster.

Kjør programmet og inspisere produksjonen i Xcode konsoll. Nedenfor kan du se hva som ble returnert, en matrise med ett objekt av typen NSManagedObject. Foretaket av objektet er Person product: [< NSManagedObject. 0x7fab71e0cee0 > (enhet: Person; id: 0xd000000000040000 < x-coredata: //E9E9FE9D-D000-4F1D-BF2C-F37CEDF5FC39 /Person /p1 >, data: < feil >)]

For å få tilgang til attributtene til posten, vi gjør bruk av nøkkel-verdi koding som vi gjorde tidligere. Det er viktig å bli kjent med nøkkelverdien koding hvis du har tenkt å jobbe med kjernedata
do {la resultat = prøve self.managedObjectContext.executeFetchRequest (fetchRequest) if (result.count > 0). {La person = resultat [0] as! NSManagedObject print ("1 - \\ (person)") hvis la først = person.valueForKey ("første"), sist = person.valueForKey ("siste") {print ("\\ (første) \\ (siste)")} print ("2 - \\ (person)")}} catch {la fetchError = feil som NSError print (fetchError)}

Du lurer kanskje på hvorfor jeg logger personen objekt før og etter å ha logget inn personens navn. Dette er faktisk en av de viktigste lærdommene fra denne artikkelen. Ta ser på resultatet under
1 - < NSManagedObject. 0x7f930b924210 > (enhet: Person; id: 0xd000000000040000 < x-coredata: //E9E9FE9D-D000-4F1D-BF2C-F37CEDF5FC39 /Person /p1 >, data: < feil >) Bart Jacobs2 - < NSManagedObject: 0x7f930b924210 > (enhet: Person; id: 0xd000000000040000 < x-coredata: //E9E9FE9D-D000-4F1D-BF2C-F37CEDF5FC39 /Person /p1 >, data: {adresser = "< forholdet skyld: 0x7f930b924150 adresser '>"; alder = 44; første = Bart, siste = Jacobs;})

Den første gangen vi logger personen objektet til konsollen, ser vi data: < feil >. Den andre gangen, men inneholder data innholdet i objektets attributter og relasjoner. Hvorfor det? Dette har alt å gjøre med forkastninger
, et nøkkelbegrep av grunndata.

5. Forkastninger

Konseptet som ligger til grunn forkastninger er ikke unikt for kjernedata. Hvis du noen gang har jobbet med Aktiv Record
i Ruby on Rails, deretter følgende vil sikkert ringe en bjelle. Konseptet er ikke identiske, men lignende fra en utviklers perspektiv.

kjernedata forsøker å beholde sin minne fotavtrykk så lavt som mulig, og en av de strategier som benyttes for å oppnå dette er forkastninger Anmeldelser . Når vi hentet postene for Person
enhet for et øyeblikk siden, Core data henrettet hente forespørsel, men det gjorde ikke fullt initial de administrerte gjenstander som representerer de hentet postene.

Hva vi fikk Ryggen er en feil, en plassholder objekt som representerer posten. Hensikten er av typen NSManagedObject og vi kan behandle det som sådan. Ved å ikke fullt initialisering posten, holder kjernedata minnet fotavtrykk lav. Det er ikke en betydelig minne sparing i vårt eksempel, men tenk hva som ville skje hvis vi hentet dusinvis, hundrevis eller kanskje tusenvis av poster.

Feil er generelt ikke noe du trenger å bekymre seg for. I det øyeblikket du tilgang til et attributt eller forholdet mellom en administrert objekt, er feilen sparken
, noe som betyr at grunndata endrer skyld inn en realisert klarte objekt. Du kan se dette i vårt eksempel, og det er også grunnen til at den andre logg uttalelse av personen objektet ikke skriver ut en feil til konsollen.

Feilende er noe som turer opp mange nykommere og jeg derfor ønsker å sørg for at du forstår det grunnleggende om dette konseptet. Vi vil lære mer om forkastninger senere i denne serien. Hvis du ønsker å lære mer om kjernedata feil, så det kan være lurt å lese dette grundig titt på forkastninger i grunndata.

6. Oppdatering Records

Oppdatering poster er like enkelt som å lage en ny rekord. Du hente posten, endre et attributt eller forhold, og lagre klarte objektet sammenheng. Fordi klarte objekt, posten, er knyttet til en administrert objekt sammenheng, er sistnevnte klar over eventuelle endringer, innsettinger, oppdateringer og sletter. Når klarte objektet sammenheng lagres, alt er overført til kompet butikken ved kjernedata.

Ta en titt på følgende kode blokken der vi oppdaterer posten vi hentet ved å endre personens alder og lagre endringene .
la person = resultere [0] as! NSManagedObjectperson.setValue (54, Forkey: "age") gjør {? Prøve person.managedObjectContext .Lagre ()} catch {la saveError = feil som NSError print (saveError)}

Du kan bekrefte at oppdateringen var vellykket ved å ta annet se på SQLite butikken som vi gjorde tidligere.

7. Slette Records

Slette en rekord følger samme mønster. Vi fortelle klarte objekt sammenheng at en rekord må slettes fra den vedvarende butikken ved å påberope deleteObject (_ :) og bestått klarte objektet som må slettes.

I vårt prosjekt, sletter personen objektet vi hentet tidligere av den sendes til den klarte objektet sammenheng er deleteObject (_ :) metoden. Legg merke til at slettingen ikke er forpliktet til å støtte butikken før vi kaller lagre () på greid objektet sammenheng.
La person = resultere [0] as! NSManagedObjectself.managedObjectContext.delete (person) gjør {prøve self.managedObjectContext.save ()} catch {la saveError = feil som NSError print (saveError)}

Du kan bekrefte at slettingen var vellykket ved å ta en titt på SQLite butikken.

Konklusjon

I denne opplæringen, har vi dekket mye mer enn bare å skape, henting, oppdatering og sletting av poster. Vi har rørt på noen viktige begreper som kjernedata er avhengig, for eksempel forkastninger og datamodell kompatibilitet.

I neste utgaven av denne serien, vil du lære hvordan du kan opprette og oppdatere relasjoner, og vi ta en grundig titt på NSFetchRequest klassen. Vi vil også begynne å bruke NSPredicate og NSSortDescriptor å gjøre våre hente forespørsler fleksibel, dynamisk og kraftfull.