Kjernedata og Swift: Relasjoner og Mer Fetching

Core Data- og Swift: Relasjoner og Mer Henter
10
Del
to
Del

Denne 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. Managed Objects og Fetch RequestsCore data og Swift: NSFetchedResultsController

I forrige artikkel, lærte vi om NSManagedObject og hvor enkelt det er å opprette, lese, oppdatere og slette poster ved hjelp av kjernedata. Men det gjorde jeg ikke nevne relasjoner i den diskusjonen. Bortsett fra noen advarsler du må være klar over, relasjoner er like lett å manipulere som attributter. I denne artikkelen vil vi fokusere på relasjoner, og vi vil også fortsette vår utforskning av NSFetchRequest.

Det jeg dekke i denne serien på kjernedata er Forutsetninger
gjelder for iOS 7 + og OS X 10.10+, men fokuset 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. Relasjoner

Vi har allerede jobbet med relasjoner i Core datamodell redaktør og hva jeg skal fortelle deg vil derfor høres kjent. Relasjoner er, akkurat som attributter, nås ved hjelp av nøkkel-verdi koding. Husk at datamodellen vi opprettet tidligere i denne serien definerer en Person
enhet og en Adresse
enhet. En person som er knyttet til en eller flere adresser, og en adresse er knyttet til en eller flere personer. Dette er en mange-til-mange
forholdet.

For å hente adressene til en person, vi bare påberope valueForKey (_ :) på person, en forekomst av NSManagedObject, og passerer i adresser som nøkkel. Merk at adresser er nøkkelen vi definert i datamodellen. Hva slags objekt du forvente? De fleste nye til kjernedata forventer en sortert array, men kjernedata returnerer et sett, som er usortert. Arbeide med sett har sine fordeler som du vil lære senere.

Opprette Records

Nok med teorien, åpne prosjektet fra forrige artikkel eller klone det fra GitHub. La oss begynne med å lage en person og deretter koble den til en adresse. For å opprette en person, åpen AppDelegate.swift og oppdateringsprogrammet (_: didFinishLaunchingWithOptions :) som vist nedenfor
func program (applikasjon: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]) - >.? Bool {//Opprett Person la entityPerson = NSEntityDescription.entityForName ("Person", inManagedObjectContext: self.managedObjectContext) la newPerson = NSManagedObject (enhet: entityPerson !, insertIntoManagedObjectContext: self.managedObjectContext) //Befolke Person newPerson.setValue ("Bart" , Forkey: "første") newPerson.setValue ("Jacobs", Forkey: "siste") newPerson.setValue (44, Forkey: "age") return true}

Dette bør se kjent hvis du har lest den forrige artikkel. Opprette en adresse ligner som du kan se nedenfor
//Opprett Addresslet entityAddress = NSEntityDescription.entityForName ("Address", inManagedObjectContext: self.managedObjectContext). La newAddress = NSManagedObject (enhet: entityAddress !, insertIntoManagedObjectContext: self.managedObjectContext) //Befolke AddressnewAddress.setValue ("Main Street", Forkey: "gata") newAddress.setValue ("Boston", Forkey: "by")

Fordi hver egenskap av Adresse
enhet er merket som valgfritt
, trenger vi ikke å tilordne en verdi til hvert attributt. I eksempelet vi bare sette rekord er gate- og attributter.

Opprette et forhold

Hvis du vil koble newAddress til newPerson, påberope vi valueForKey (_ :), passerer i adresser som nøkkel. Verdien som vi passerer i en NSSet forekomsten som inneholder newAddress. Ta en titt på følgende kodeblokken for avklaring
//Legg til adresse til PersonnewPerson.setValue. (NSSet (objekt: newAddress), Forkey: "adresser") {? Se newPerson.managedObjectContext .Lagre ()} do fangst { la saveError = feil som NSError print (saveError)}

Vi kaller redning () på greid objekt sammenheng med newPerson å forplante endringene i vedvarende butikken. Husk at du ringer lagre () på en administrert objekt sammenheng sparer staten forvaltet objektet sammenheng. Dette betyr at newAddress er også skrevet for å støtte butikken samt relasjonene vi bare definert.

Du lurer kanskje på hvorfor vi ikke knytte newPerson til newAddress, fordi vi gjorde definere en invers sammenheng i data modell. Kjernen data skaper dette forholdet for oss. Hvis et forhold har en invers sammenheng, da kjernedata tar seg av dette automatisk. Du kan kontrollere dette ved å spørre newAddress for sine personer.

Henter og oppdatere en Relationship

Oppdatering et forhold er ikke vanskelig heller. Det eneste forbeholdet er at vi må legge til eller fjerne elementer fra uforanderlige NSSet eksempel kjernedata hender til oss. For å gjøre denne oppgaven enklere, men erklærer NSKeyValueCoding protokollen en praktisk metode mutableSetValueForKey (_ :), som returnerer et NSMutableSet objekt. Vi kan da bare legge til eller fjerne et element fra samlingen for å oppdatere forholdet.

Ta en titt på følgende kode blokken der vi skaper en annen adresse, og forbinder det med newPerson. Vi gjør dette ved å påberope mutableSetValueForKey (_ :) på newPerson og legge otherAddress til foranderlig sett. Det er ikke nødvendig å fortelle kjernedata som vi har oppdatert forholdet. Kjernen data holder styr på foranderlig sett at det ga oss og oppdaterer forholdet
//Opprett Addresslet otherAddress = NSManagedObject (enhet: entityAddress !, insertIntoManagedObjectContext: self.managedObjectContext). //Set Først og Sist NameotherAddress.setValue (" 5th Avenue ", Forkey:" gata ") otherAddress.setValue (" New York ", Forkey:" by ") //Legg til adresse til Personlet adresser = newPerson.mutableSetValueForKey (" adresser ") addresses.addObject (otherAddress)
Slette et forhold

Du kan slette et forhold ved å påberope SetValue (_: Forkey :), passerer null som verdien og navnet på forholdet som nøkkel. I neste kodebiten, oppheve tilknytningen vi hver adresse fra newPerson
//Slett RelationshipnewPerson.setValue. (Null, Forkey: "adresser")
2. En-til-en og en-til-mange relasjoner

En-til-en-relasjoner

Selv om vår datamodell ikke definerer et en-til-en-relasjon, har du lært alt du trenger å vite for å jobbe med denne type forhold. Arbeider med en en-til-en forhold er identisk til å samarbeide med attributter. Den eneste forskjellen er at verdien du kommer tilbake fra valueForKey (_ :) og verdien du sender til SetValue (_:. Forkey :) er en NSManagedObject eksempel

La oss oppdatere datamodell for å illustrere dette. Åpne Core_Data.xcdatamodeld Hotell og velg Person
enhet. Opprett et nytt forhold og gi den navnet ektefelle
. Sett Person
enhet som destinasjon og sette ektefelle
forhold som den inverse forholdet.

Som du kan se, er det mulig å skape et forhold hvor destinasjonen av forholdet er samme enhet som den enhet som definerer forholdet. Merk også at vi alltid satt den inverse av forholdet. Som Apples dokumentasjon stater, er det svært få situasjoner der du ønsker å opprette et forhold som ikke har en invers sammenheng.

Vet du hva som vil skje hvis du skulle bygge og kjøre programmet? Det er riktig, vil programmet krasje. Fordi vi endret datamodellen, eksisterende backing butikken, en SQLite database i dette eksempelet, er ikke lenger forenlig med datamodellen. For å bøte på dette, fjerne programmet fra enheten eller simulatoren og kjøre programmet. Ikke bekymre deg om, vil vi løse dette problemet mer elegant i en fremtidig avdrag hjelp vandringer.

Hvis du kan kjøre programmet uten problemer, så er det tid for neste trinn. Head tilbake til programmet delegat klassen og legge til følgende kode blokken
//Opprett en Personlet anotherPerson = NSManagedObject. (Enhet: entityPerson !, insertIntoManagedObjectContext: self.managedObjectContext) //Set Først og Sist NameanotherPerson.setValue ("Jane" , Forkey: "første") anotherPerson.setValue ("Doe", Forkey: "siste") anotherPerson.setValue (42, Forkey: "age")

Hvis du vil angi anotherPerson som ektefelle av newPerson, påberope vi SetValue (_ : Forkey :) på newPerson og passere i anotherPerson og "ektefelle" som argumenter. Vi kan oppnå samme resultat ved å påberope SetValue (_:. Forkey :) på anotherPerson, passerer i newPerson og "ektefelle" som argumenter
//Opprett RelationshipnewPerson.setValue (anotherPerson, Forkey: "ektefelle")
En-til-mange relasjoner

La oss avslutte med en titt på en-til-mange-relasjoner. Åpne Core_Data.xcdatamodeld
, velg Person
enhet, og skape en relasjon med navnet barn
. Sett reisemålet til Person
, angi typen til Slik Mange
, og la den inverse forholdet tom for nå.

Opprett et annet forhold som heter far
, angi dette som reisemål til Person
, og sette den inverse forholdet til barn
. Dette vil automatisk fylle den inverse forholdet mellom barn
forholdet vi tom for et øyeblikk siden. Vi har nå laget en en-til-mange-relasjon, det vil si en far kan ha mange barn, men et barn kan bare ha én biologisk far.

Leder tilbake til programmet delegat og legge til følgende kode blokkere. Vi skaper en annen Person
posten, angi attributter, og sett det som et barn av newPerson ved å spørre Kjerne Data for et foranderlig sett for nøkkel barn og legge den nye plata til foranderlig sett. Twitter //Opprett et barn Personlet newChildPerson = NSManagedObject (enhet: entityPerson !, insertIntoManagedObjectContext: self.managedObjectContext) //Set Først og Sist NamenewChildPerson.setValue ("Jim", Forkey: "første") newChildPerson.setValue ("Doe", Forkey: "siste") newChildPerson.setValue (21, Forkey: "age") //Opprett Relationshiplet barn = newPerson.mutableSetValueForKey ("barn") children.addObject (newChildPerson)

Følgende kode blokk oppnår samme resultat ved å sette far egenskap av anotherChildPerson. Resultatet er at newPerson blir far til anotherChildPerson og anotherChildPerson blir et barn av newPerson
//Opprett en Child Personlet anotherChildPerson = NSManagedObject (enhet: entityPerson !, insertIntoManagedObjectContext: self.managedObjectContext). //Set Først og Sist NameanotherChildPerson. SetValue ("Lucy", Forkey: "første") anotherChildPerson.setValue ("Doe", Forkey: "siste") anotherChildPerson.setValue (19, Forkey: "age") //Opprett RelationshipanotherChildPerson.setValue (newPerson, Forkey: " far ")
3. Mer Henter

Datamodellen av vår prøveprogrammet har vokst ganske mye i form av kompleksitet. Vi har opprettet en-til-en, en-til-mange og mange-til-mange relasjoner. Vi har sett hvor enkelt det er å lage poster, inkludert relasjoner. Hvis vi ønsker også å være i stand til å trekke at data fra den vedvarende butikken, så trenger vi å lære mer om henting. La oss starte med et enkelt eksempel der ser vi hvordan å sortere resultatene som returneres av en hente forespørsel.

Sorter beskrivelsene

Hvis du vil sortere postene vi får tilbake fra greid objektet sammenheng, bruker vi den NSSortDescriptor klassen. Ta en titt på følgende kodebiten
//Opprett Fetch Requestlet fetchRequest = NSFetchRequest (entitetsnavn: "Person"). //Legg Sorter Descriptorlet sortDescriptor = NSSortDescriptor (key: "første", stigende: true) fetchRequest.sortDescriptors = [sortDescriptor] //Execute Fetch Requestdo {la resultat = prøve self.managedObjectContext.executeFetchRequest (fetchRequest) for managedObject i følge {if la først = managedObject.valueForKey ("første"), sist = managedObject.valueForKey ("siste") { print ("\\ (første) \\ (siste)")}}} catch {la fetchError = feil som NSError print (fetchError)}

Vi initial en henteforespørsel ved å sende i foretaket som vi er interessert i, < b> Person
. Vi deretter opprette en NSSortDescriptor objekt ved å påberope init (key: stigende :), passerer i egenskap av foretaket vi ønsker å sortere etter, først, og en boolsk angir om postene må sorteres i stigende eller synkende rekkefølge.

Vi knytte slags descriptor til hente forespørsel ved å sette sortDescriptors eiendom hente forespørsel. Fordi sortDescriptors Eiendommen er av typen [NSSortDescriptor] ?, det er mulig å angi mer enn én slags beskrivelse. Vi vil ta en titt på dette alternativet i et øyeblikk.

Resten av kodeblokken bør se kjent. Hente forespørselen sendes til den administrerte objektet sammenheng, som utfører hente forespørsel når vi påberope executeFetchRequest (_ :). Husk at sistnevnte er en kaste metode, noe som betyr at vi bruker prøve søkeord og utføre hente forespørselen i en gjør-fangst uttalelse.

Kjør programmet og inspisere produksjonen i Xcode konsoll. Utgangen bør ligne på det som vises nedenfor. Som du kan se, er postene sortert etter sitt fornavn.
Bart JacobsJane DoeJim DoeLucy Doe

Hvis du ser duplikater i produksjonen, så sørg for å kommentere ut koden vi skrev tidligere for å skape postene. Hver gang du kjører programmet, er de samme postene opprettet, noe som resulterer i like poster.

Som jeg nevnte, er det mulig å kombinere flere sorterings beskrivelsene. La oss sortere postene ved deres etternavn og alder. Vi først sette nøkkelen i den første slags descriptor å vare. Vi deretter opprette en annen slags descriptor med en nøkkel av alder og legge den til i rekke sorterings beskrivelsene
//Legg Sorter Descriptorlet sortDescriptor1 = NSSortDescriptor. (Key: "siste", stigende: true) la sortDescriptor2 = NSSortDescriptor (nøkkel: "alder", stigende: true) fetchRequest.sortDescriptors = [sortDescriptor1, sortDescriptor2]

Utgangen viser at rekkefølgen på sorterings beskrivelsene i matrisen er viktig. Postene er først sortert etter deres etternavn og deretter av deres alder.
Lucy Doe (19) Jim Doe (21) Jane Doe (42) Bart Jacobs (44)
predikater

Sorter beskrivelsene er stor og lett å bruke, men predikater er det som virkelig gjør henting kraftig i kjernedata. Sorter beskrivelsene forteller kjernedata hvordan postene må sorteres. Predikater fortelle kjernedata hva registrerer du er interessert i. Den klassen vi skal jobbe med er NSPredicate.

La oss starte med å hente hvert medlem av Doe familien. Dette er veldig enkelt å gjøre og syntaksen vil minne noen av dere av SQL
//Fetchinglet fetchRequest = NSFetchRequest (entitetsnavn: "Person"). //Lag Predicatelet predikat = NSPredicate (format: "% K ==% @ "," siste "," Doe ") fetchRequest.predicate = predikatet //Legg Sorter Descriptorlet sortDescriptor1 = NSSortDescriptor (key:" siste ", stigende: true) la sortDescriptor2 = NSSortDescriptor (key:" alder ", stigende: true) fetchRequest .sortDescriptors = [sortDescriptor1, sortDescriptor2] //Execute Fetch Requestdo {la resultat = prøve self.managedObjectContext.executeFetchRequest (fetchRequest) for managedObject i følge {if la først = managedObject.valueForKey ("første"), sist = managedObject.valueForKey ( "siste"), alder = managedObject.valueForKey ("age") {print ("\\ (første) \\ (siste) (\\ (alder))")}}} catch {la fetchError = feil som NSError print (fetchError) }

Vi har ikke forandret seg mye bortsett fra å lage en NSPredicate objekt ved å påberope init (format: argumenter :) og knytte predikatet til hente forespørsel ved å sette sistnevntes predikat eiendom. Merk at init (format: argumenter :) metode aksepterer et variabelt antall argumenter

predikat formatstrengen bruker% K for eiendom navnet og% @ for verdien.. Som nevnt i Predicate Programming Guide, er% K en variabel argument erstatning for en nøkkelbanen mens% @ er en variabel argument erstatning for et objekt verdi. Dette betyr at predikatet format streng med vårt eksempel vurderer å vare == "Doe"

Hvis du kjører programmet og inspisere produksjonen i Xcode konsollen, bør du se følgende resultat:.
Lucy Doe (19) Jim Doe (21) Jane Doe (42)

Det er mange aktører vi kan bruke for sammenligning. I tillegg til = og ==, som er identiske så langt som kjernedata er opptatt av, det er også > = og = >, < = og = >, = og <! ≫, og > og <. Jeg oppfordrer deg til å eksperimentere med disse aktørene for å lære hvordan de påvirker resultatene av hente forespørsel

Følgende predikat illustrerer hvordan vi kan bruke >. = Operatøren å bare hente Person poster med en alder attributt større enn 30.
la predikat = NSPredicate (format: "% K > =% i", "alder", 30)

Vi har også operatører for streng sammenligning INNEHOLDER, SOM, fyrstikker, BEGINSWITH, og ENDSWITH. La oss hente hver Person posten hvis navn inneholder bokstaven j
la predikat = NSPredicate. (Format: "% K INNEHOLDER% @", "første", "j")

Hvis du kjører programmet, rekken av Resultatene vil være tom siden strengen sammenligningen er store og små bokstaver som standard. Vi kan endre dette ved å legge til en endrings som så:
la predikat = NSPredicate (format: "% K INNEHOLDER [c]% @", "første", "j")

Du kan også lage sammensatte predikater ved hjelp stikkorda AND, OR og NOT. I følgende eksempel, vi hente hver person med fornavn inneholder bokstaven j og er yngre enn 30.
la predikat = NSPredicate (format: "% K INNEHOLDER [c]% @ og% K <% i", "første", "j", "alder", 30)

predikater også gjør det svært enkelt å hente poster basert på forholdet deres. I følgende eksempel, vi hente hver person som fars navn er lik Bart
la predikat = NSPredicate. (Format: "% K ==% @", "father.first", "Bart")

ovenfor predikat fungerer som forventet, fordi% K er en variabel argument erstatning for en -tasten banen
, ikke bare en -tasten
.

Hva du trenger å huske er at predikater gjør det mulig å søke i kompet butikken uten at du vet noe om butikken. Selv om syntaksen til predikatet formatstrengen minner om SQL på noen måter, det spiller ingen rolle om backing butikken er en SQLite database eller en in-memory butikken. Dette er en svært kraftig konsept som ikke er unik for kjernedata. Rails Active Record er et annet godt eksempel på dette paradigmet.

Det er mye mer å predikater enn det jeg har vist deg i denne artikkelen. Hvis du ønsker å lære mer om predikater, foreslår jeg at du tar en kikk på Apples Predicate Programming Guide. Vi vil også jobbe mer med predikater i de neste artiklene i denne serien.

Konklusjon

Du har nå en god forståelse av det grunnleggende kjernedata, og det er på tide å begynne å jobbe med rammeverk ved å lage et program som utnytter kjernedata makt. I neste artikkel, møter vi en annen viktig klasse av Core data rammeverket, NSFetchedResultsController. Denne klassen vil hjelpe oss å administrere en samling av poster, men du vil lære at det gjør ganske mye mer enn det.