Tip:
Highlight text to annotate it
X
[ Music ]
>> Stanford University.
>> Okay, well welcome to CS193p, Lecture 12 of fall 2013 and 14,
and today we have one topic
and one topic only, which is core data.
And, as someone was asking earlier, assignment 5 is due
on Wednesday and then your last assignment, assignment 6,
will go out then and be due a week later.
ON Wednesday, I'm going to be going
over the final project requirements and we're going
to continue with core data a little bit, talk about core data
with table, UI table view.
And then I'm going to do a big demo.
So today's lecture is going to be all slides.
I don't usually like to do that just because it's an awful lot
of slides all at once, but it makes sense in this case
and I'll be doing the demo for today's stuff on Wednesday,
and then next week we'll start talking about advanced segwaying
and maybe we'll do some map kit, multitasking,
I'm not quite sure what I'm going to cover next week
because you won't have a homework assignment ton that.
This core data stuff that we're covering today and Wednesday,
that's going to be the topic of your last homework assignment
that goes out on Wednesday.
So, core data, what is core data?
Core data is an object-oriented database.
Why do we need it?
Because in any kind of significant application
where we have a lot of data to crunch on,
we can't be storing it in NS user defaults or not storing it
at all, or always fetching it down from Flickr, right?
So your current assignment, for the Shutterbug demo
that we did last week,
we're just always [pause] fetching our entire model,
basically, every time we want it from Flickr,
but what if we had a very large database of photos, thousands
or tens of thousands of little pieces of information
about photos, we couldn't be fetching that down every time
and we couldn't store it in NS user default
so we need a real database.
Plus, a read database allows us to make really smart queries
about what's, our data is right now.
We can't really query it at all, or almost not at all,
we're just kind of displaying it all in tables,
so to take the next level of sophistication
in our app, we need to do that.
So, core data is a very, very powerful framework in iOS7,
one of the most powerful in terms of what it can do.
I can only just barely scratch the surface
in a lecture, in a little bit.
This week, I'm just going to try and get you the basics
and you're going to have to, you know,
if you want to do a serious database app in your future,
maybe for your final project or beyond,
you'll need to do a little bit of extra reading
up because core data is massive, okay?
But fundamentally, it's a base, it's a way of creating objects,
objective C objects that you are going to deal
with that are mapped, or linked, to SQL, or XML, databases, okay?
So, it's kind of a bridge between object-oriented land
and database land, and database land is dominated
by things like sequel.
Okay? How many people here know what SQL is?
Not know how to program SQL, but know what it is?
Yeah, so everyone, pretty much.
So that's mostly what core data is hooked
up is, is sequel backend.
So how does core data work?
First, you create a visual map using a tool in Xcode.
Okay? So dragging and dropping and all that stuff,
so creating a mapping between an object-oriented view
of your data and the sequel or tables
and rows [pause] version of your data.
And core data allows really
that to be pretty seamless once you've created this mapping,
and we'll talk all about that and how it does that,
but let's start by looking how we create this visual map
in Xcode.
Okay? So, first, we create the map by doing new file,
anytime we want to add something new to our project,
we're going to do new file, but in new file, instead of doing
like Cocoa Touch, objective C class, we're going to go down to
where it says core data, see that?
And pick data model, don't accidentally pick mapping model,
its data model that you want here, okay,
the data model is the mapping
between the object-oriented world and the database world.
So you click that, it's going to ask you where, what you want
to call it and where you want to put it.
Some people like to call their model file, model,
if they only have one.
Some people like to name it the name of the application.
So like of last week, if we were doing core data we might have
called Shutterbug, the name.
Sometimes you might have multiple mappings,
and it is totally possible
and in a big application you might well have multiple
databases, multiple mappings,
and so you would have multiple files, so you want to pick names
for your mappings that kind of correspond
to what their purpose is inside your application,
so here we're going to take the default, which is just model,
and you can see there, it creates this new file.
If you look over in the navigator over there,
model dot xc data model d. Okay?
And this is our mapping, our visual mapping file,
and you can see here that it has some components, and we're going
to talk about most of them.
The components are entities, attributes,
relationships, and fetch properties.
Okay?
So entities are kind of like objects, they're going
to map to our objects.
In the database world, they are tables, okay?
Attributes are kind of like the columns in a table in database.
In our object-oriented world, they're going
to be properties on objects, okay?
Relationships are also properties on objects,
but they are pointers to other objects in the database, okay,
or pointers to a bunch of other objects, okay,
so this would be kind of like joins between tables
in the database sense.
Fetch properties are kind of a calculated way to have a pointer
to some other properties.
I just have to cut something for time, so we're not going to talk
about fetch properties, they're really not
that complicated once you understand how fetching works,
much later in this lecture then you'll kind of get the idea.
Oh, I can see how maybe fetch properties would work
and then you can read the documentation and figure it out.
So, that's what the data model consists of, entities,
attributes, relationships.
That's what we're going to talk about.
Let's, for our example today,
let's say we had a Shutterbug-like application
that had photos and photographers.
Okay? So your homework is going to be more places and photos.
Our demo here, and the demo I do on Wednesday, and the slides,
are going to be photos and photographers.
So those are the two things that are going to be in our database,
photos and photographers.
So, let's create an entity for the photos.
So we click add entity down there at the bottom
and we get this entity.
I'm going to change the name from entity to photo,
so this is going to be my photo object in the database.
And then I can add attributes to my photo object by clicking
on that little plus underneath where it says attributes there.
And when I add one, it adds one called attribute,
I'm going to change the name of that to title,
so photos have a title, so I'm going to put a title in there.
And you notice as I do this, I get an error,
a little red error warning up there.
Why is that?
That's because the type of my title is undefined.
You see where it says type, undefined next to title there?
So that's not allowed.
You have to specify the type of all the properties
in your database, and so you just click on that undefined,
I'm going to set this one to be a string, the title,
obviously, is a string.
Now, what can some of these things be, besides strings?
Well they can be numbers, either integers
or floating point numbers,
those are all representing the database as,
in your object world rather, as NS numbers.
Okay? All your properties, in fact, all the attributes
in your objects in the database, all their properties are going
to be objects of some sort.
So NS string objects if their strings.
NS numbers if they're integers, floats, or double, or bullions.
NS date if they're dates.
NS datas if they're binary data, that's the second to the bottom.
And then transformable is kind of an interesting one,
those will be NS data objects,
which will provide a little transformable object
that will convert them to some other type,
like even a [inaudible] struct, or something like that,
and we're not going to talk about transformables,
but it is possible to store really any data type
in the database as long as you provide a transformation object
that will convert them back and forth between NS data.
Okay? So here, title is a simple one, it's a string,
I'm going to add a whole bunch of other attributes here
that you kind of look, no more error there right now,
that I set it as string there's no more error.
If I add a whole bunch more,
you can see I've added an upload date that I got
from Flickr, so that's a date.
Even thumbnail data, so let's say I downloaded the thumbnail
image data, the image date for a little thumbnail of my photo
from Flickr, I can actually put that data
in the database as an NS data.
Some people ask, how big an NS data could I put in there?
For example, could I put an NS data that is the real photo,
the big, you know, maybe a megabyte-size photo,
and the answer is absolutely, you can put them in here,
in fact, there's a little switch that you can turn
on that says I'm going to put something big here,
store it in another file.
Okay? But even if you store it in the sequel file,
it can usually manage it, it's probably not the best,
most efficient way to do it, but yes,
you can store big data blobs in core data, as well.
So, there's some example attributes.
Let's look at this a different way.
If you see down at the bottom there
where it says editor style, right, I clicked on that
and it switched over to showing the same thing,
but in a graphical format.
So you see, here's my photo entity,
I can see the attributes, see them there, listed there?
And I can still create an entity in this view, so I'm going
to create another entity, which is my photographer.
I create that, entity, I'm going to change the name
to photographer, it changes it.
Okay? I can also add attributes in this view.
So I just go down to this button here, add attribute,
I'm going to add an attribute here and I'm going
to call it name, so that's the name of the photographer.
You can also edit these attributes,
here in this view, by clicking on them.
Alright, by clicking on the name, and then going
up to this attributes inspector up here, okay?
So this is kind of like the attributes inspector
in other environments inside of Xcode, so if I click on that,
you can see I get a bunch of attributes of my attributes.
So here's the name one, and you can see there's the name of it
and there's the attribute type, which you can see is undefined,
that's why we have the error up there.
It's the attribute type undefined.
So I'm going to click on that popup right there and change
that undefined to be string.
The photographers name is also a string.
Okay? So we got that.
I can also create relationships between these objects,
photos and photographers, clearly a photo
and a photographer have a relationship, right?
A photographer takes photos.
A photo is taken by a photographer.
So I can create relationships between them by control,
dragging, that's how we like to do things
in Xcode, we control, drag.
So I'm going to control,
drag between the photo and the photographer.
And it's going to create this new relationship between them.
Now, the relationship will have a different name on each side.
On the photo side, we're going to call this relationship
who took, because this relationship is
for the photo, who took it.
And on the photographer side, we're going to call to photos,
because a photographer can take multiple photos,
so we'll call it photos.
Okay? So this is the same relationship, but it's going
to have a property on each of these two objects,
photos and photographers, it's a little different.
Now, notice that from a photographer's standpoint,
this relationship is not just a one-to-one,
because a photographer can take multiple photos,
and you can see them in the inspector for photos,
on photographer, there's a type that says 2, 1, okay?
That means it's a 1, 2, 1, relationship.
I can actually change that to too many, and when I do that,
you're going to see that I get a double arrow head, on that side
of the pointer there,
that's saying a photographer can have multiple photos.
Now, we talked about how title is a string
and upload date is a date, what type is who took and photos?
What type are those?
Because they're just properties, as well, of these objects,
but what's their type?
And it turns out that something like photos,
which is a too many, is an NS set.
Okay? That's going to be its type.
Just like title is a NS string, photos is an NS set.
What about who took?
Who took turns out to be a NS-managed object star,
so it's a pointer to an NS-managed object.
NS-managed object is the super class for all objects
in the database, okay, so photo is going
to be an NS managed-object, a photographer is going
to be a NS-managed object.
In our objective C code, they're all going
to be NS-managed objects, so, of course, the who took pointer,
which is a pointer to a photographer,
is going to be an NS-managed object star, and we're going
to see how we can subclass NS-managed object
to add all these properties, and all this stuff,
and then it would be a photographer star,
but that's what who took is.
It's going to be exactly what you would expect, it's a pointer
to an object, which is that other object.
Okay? [Pause] This little delete rule right there, I'm not going
to go into detail about this,
but the delete rule specifies what to do to who took, okay,
this is the delete rule for photos,
you see I'm inspecting photos there, this is the delete rule
that says if I delete a photographer,
what happens to who took?
Okay? Because who took was pointing to this photographer
and if I delete that photographer from the database,
and the answer here, this delete rule is nullify,
which means who took would be set to nil.
Okay? But there are other rules, for example, cascade,
which means delete, when you delete the photographer,
delete every single photo that it points to.
Alright? Cascade the delete throughout the database.
So, you really want to know what you're doing here.
The safest one is usually nullify, set things to nil,
but if your database doesn't make sense if a pointer points
to nil, you might need more complicated delete rules there,
but, in your homework, you won't need anything more complicated
than that.
So that's basically how we build this map between objects
and the database, okay?
So, there's a lot of things
that you can do once you have this map.
We're going to focus on the creating entities, attributes,
and relationships, and then setting their attributes
and then querying for them and things like that.
But there's a lot of other things you can do in core data,
I'll mention some of them at the end and you can go read up on it
if you want to do more sophisticated database things.
So, how do we access all of this stuff in our code,
once we've created this map?
And the answer is we need an NS-managed object context.
Okay? An NS-managed object context.
This context is kind of the hook we need to go create objects
in the database, set attributes of objects in the database,
query for objects for the database,
we all do these through this context.
So, how do we get a context?
Well, there's really two ways to do it.
One is you use this class UI managed object, which I'm ,
or managed document rather, which I'm going to talk about.
And the second way, which I'm not going to talk about,
is that you can alloc init a NS-managed object context.
The reason I'm not going to talk about it is,
you got to know a little bit about context and how they work
and how they do thread safety and things like that,
that I really don't want to get into the details of,
and that UI-managed document takes care of it all for us.
Okay?
So that's why I'm going to focus on the UI-managed document way.
If you want to see how the way
of alloc initing an NS-managed object context,
because there's more then that you need to do, like you've got
to specify where the file is, the persistent store for it,
you got to get this map into the picture.
All that stuff you'd have to do manually
if you don't use UI-managed document.
If you go when creating a new project
and say master detail instead of single view controller,
it'll actually have a button that says use core data
and when you create that new one, you'll see a whole bunch
of core data code that does all this business in there.
Okay? But, there's no need for you to go look at that,
UI-managed document is a much simpler,
cleaner way to get yourself a UI,
or an NS-managed object context,
which is what we're trying to do.
So let's talk about UI-managed document and how that works.
It inherits from a class called UI document.
UI document is a whole bunch of mechanism
for managing some storage, okay?
UI-managed document puts a core database in some storage.
So you're managing the storage of your core data database,
so you can think of UI-managed document as just like a thing
that contains your core data database for you.
And all you ever really do is open, or save, or open
and create, a UI-managed document
and then grab its managed object context,
and then use it to do your database.
That's it.
Okay? So that's what we're going to talk about.
How to open, or create, a UI-managed document,
and then get its managed object context and we'll dive back
into now that we have a context, how do we do core data?
So here's how you create a UI-managed document.
You alloc init it.
Its designated initializer is init with file URL,
you have to give it the URL
where this core data database is going to be stored, okay?
I've put these four lines of code that are in white,
that are before the yellow here,
they describe how you would create a document called My
Document in the users documents directory, okay?
So when you're, we haven't really talked
about storing files, right?
We haven't accessed the file system at all,
in anything we've ever done.
We do this with this class NS file manager.
I'm not really going to cover that this week.
Suffice it to say the file manager can give you the URL
of the documents directory, for the user,
that's what the second line of code there is,
and then you can just append whatever you want the name
of your document to be, like my document or Flickr database,
or whatever you would want to call it.
This is going to be the URL where it's going to put
that core data database for you.
So now you have the URL, you just say alloc init
to create a UI-managed document.
Okay? So, this UI-managed document though,
you've only alloc init it, you haven't actually created it
on disk, it doesn't exist.
It doesn't exist on disc yet, okay?
So, we've got some work to do to make this thing exist on disc,
and how do we do that?
Well, we have to create it if it doesn't exist,
and we have to open it if it does exist.
In other words, we've used it before.
So to find out whether it exists on disc,
we have to do this file manager thing, file exist that path.
So you have the URL to it, you say,
NS file manager default manager,
that's like a shared file manager.
File existed path, it returns a bullion whether the file
already exists.
If it does exist, in other words the file exists,
then we're going to open it, and the way we're going
to open it is with the UI-managed document method open
with completion handler.
If it doesn't exist, then we're going to create that document
on disc with save to URL.
For save operation, UI document save
for creating is the argument we want there,
because we're creating this file, completion handler.
Okay? What's that completion handler business all about?
Okay, why do these open and save and create this, it says save
to URL, but really it's create at URL.
Why do they have this completion handler?
Well, it's because these are asynchronous.
These two methods open and create, basically,
are asynchronous, they don't happen immediately.
When you ask the UI-managed document please open this
or please create this for me,
it might take it a little bit of time.
Okay? Why might it take some time?
Well, it's possible it could take some time
because the file system takes a certain amount of time, but,
yeah, there's other things involved that we're not going
to talk about, like iCloud, where maybe it needs
to go check something with some network cache or something
like that, because this document is going to be on iCloud,
okay, in other devices.
Now, one great thing about UI-managed document and using
that to do your core data database,
is you're on the fast-track to iCloud.
Okay? iCloud much easier, more straightforward
if you just start with UI-managed document.
But anyway, suffice it to say, these two things,
opening a document and creating document, are asynchronous,
which means that completion handler,
that little syntax there,
but that completion handler gets called back
on the thread you called these methods on, which has got
to be the main thread because this is UI-managed document,
this is a UI kit object, you have to call these methods
on the main thread, the main queue.
So that completion handler will get called back
on the main queue when it's done.
Okay? No matter how long it takes, it'll get called later
when it's done, and then in that block,
you just start doing what you want to do.
Alright? So let's talk about what you might,
how this might look to [pause] see if the file exists
and if it does open it and if it doesn't, create it,
it looks something like this.
And inside that completion handler,
if you successfully opened or successfully created it,
you're going to call some method of yours,
I've called it document is ready, you can call
that method anything you want, that says okay,
the documents ready to go, now you can go use it.
Alright? And I'm doing the same thing in either case.
It's a little bit of a bummer that there's no method like open
or create, and it just does it all in once, that you have
to do this file exist business, but you do, so you're going
to have this little sequence of code in almost any app
that uses a UI-managed document, okay?
It's kind of silly, because it's all very, very simple, similar,
if then there, but that's what it looks like, okay?
So now, what does that document is ready method look like?
It looks something like this.
One thing I might do at the beginning
of my document is ready is check the state of the document to see
if it's in the normal state,
normal state means this guys is ready to go.
Okay? What other states could it be in?
Well it could be in the state closed,
closed means you haven't opened it yet
or you haven't created it yet.
Either case it's going to be closed.
You cannot use a document once in the closed state.
Some of these other states, like there was a saving error
or editing is temporarily disabled
because someone else is editing this document in iCloud
and its updating, for example.
You're not going to run into those in this class,
but you should know that they are there.
So, checking that, at the beginning to, you know,
it's a bit mask there, that's why I'm using that ampersand,
right, I'm just checking that bit,
do my documents stay normal?
Just to see if the document is ready to go.
So that might be the first thing I do is check its state
to make sure it's ready to go, if it's not,
like if it was closed, then I might go back and try
to open it, or create it again.
If it's got an error, I might wait awhile and try this again,
or something like that.
So anyway, let's say I have this document in a normal state,
now what am I going to do?
Now I'm going to grab that NS-managed object context
and go start doing some core data stuff.
And that's it.
Okay? So this is how we get started.
Now, before we dive into what we're going to do
with that NS-managed object context, a couple more things
about UI-managed document, one is that it is auto saved, okay?
You do not have to save it.
And you can save it using this method you see here,
but generally we don't, we let it auto save.
Okay? The way we save it is with the same method
that we used before, but we say UI document save
for overwriting, but generally, we don't call this method.
We let it auto save.
Okay? So that's an important thing to understand.
Okay? Second thing, closing the document.
Well, it kind of auto closes, as well.
When does it close?
When there are no more strong pointers to it.
In other words, when there are no more strong pointers to it,
when it's going to leave the heap,
it gets closed automatically.
So you generally don't call a close either,
but you can do that as well.
And close, just like saving, are asynchronous,
and you got to implement these completion handlers
to do whatever you want
when they're done closing or done saving.
Question?
[ Inaudible Background Question ]
Correct. Yes.
So the question, does it auto save before closing?
Absolutely.
If you say close, or if you stop having a strong pointer to it,
it will save and then close.
Okay? One thing I will say about saving, the auto saving,
when you're in debugging, you know,
when you're in development mode and you're debugging,
sometimes students will get a little confused,
because they'll go and they'll do something,
update their database, and then they'll stop in the debugger,
made a code change and run again,
and the data won't be there.
Well, because it didn't have a chance to auto save.
So when you're in develop mode, you might want to throw
in some explicit saves if you're going to be hitting stop
in the debugger all the time.
Okay? Alright, so let's talk about multiple instances
of UI-managed document on the same document.
Am I allowed to alloc init file URL
to create a UI-managed document, and then somewhere else
in my code, alloc init file URL, the same URL, and have two
of them looking at the same document, you might think,
oh that can't work, but actually it does,
it works perfectly fine.
Okay? It works absolutely no problem,
the two have their own managed object context
that are different, but they're both, contexts are looking
at the same database and when they both make changes,
it's going to save.
There is the possible problem that one set of changes
in this instance of the UI-managed document,
and this one might conflict.
They might be trying to, one's trying to delete an object,
but another one is trying to set an attribute
of it at the same time.
So, if you do have this, it is possible to get conflicts,
but usually if we have multiple instances,
it's because we have one writer and many readers.
It's pretty rare to have two separate controllers
or something, each having a document
and their both making changes, that's pretty rare.
Okay? Especially both making changes at the same time,
there's only so much screen for changes to be being made on.
One thing to be careful of here is
that if one document changes the database,
the other one doesn't automatically see those changes.
Okay? It's not like oh I added an object
in one managed document, oh, I don't see it in the other one.
Why don't you see it?
Well you don't see it because they have different context,
different NS-managed object contexts,
but you're still modifying the same database.
So how could you find out, if you wanted to find
out if another document is doing,
and the answer is, the radio station.
Okay? We could use NS notification.
And so, here's an example of having a controller,
when it comes on screen, start listening at,
to the managed object context, there, of a different document.
Not the document it has open, but some other [pause],
some other instance of a UI-managed document,
and it's looking for the radio station,
NS-managed object context did save notification.
So whenever that other document auto saves, then it's going
to get a NS notification saying oh, it changed.
Alright? And I'm going to call this method context changed
or whatever.
And what would I do when that happens?
Well, two things, one, I could just refetch all my objects.
Okay, I know the database is changed,
I'll just refetch them all, we haven't talked about fetching,
but I could refetch and get a fresh set of everything.
But, actually this context change did save notification,
gives you an array, inside of its user info is a dictionary,
with three arrays that are a list
of all the objects that changed.
Okay? And you can merge those changes
into your context using this NS-managed object context
method, merge changes from context did save notification,
you just give it the notification,
and it'll automatically merge all those changes
into your context, which is pretty cool.
This is a very cool method.
Very underappreciated coolness of this method, okay?
So you're watching some other context that's
on the same database as yours, but it's a different context,
you get a notification, you can merge those changes into yours,
as if you had made those changes.
And it's all going to be fine when both documents save
because you're merging in the same changes and this is,
core data automatically deals
with when the same change is being made
by two different contexts,
only when conflicting ones happen is it a problem for you.
Okay? So this all kind of just magically works.
So that's watching another context.
Probably not going to be necessary in homework,
because you're probably going to take a different strategy,
which is have one managed document, UI-managed document,
and you use its context everywhere in your app.
That's kind of a simple way to do it,
and that's what I recommend for your homework.
And we'll see in the demo, I'm going to do that in the demo.
Alright. So now we have an NS-managed object context
that we got from our document, what can we do with it?
Well, we can insert and delete objects,
we can change attributes, and we can query for objects.
So let's talk about doing all those things.
Let's start with inserting.
So let's say I want to put a new photo, or put a new photographer
into the database, I do that with this method right here,
it's a class method on NS entity description called insert new
object for entity for name in managed object context.
So you can see, I can't insert an object
without having a context.
The context is the hook that lets you insert things
in the database or query or anything,
so you have to have a context.
And, that first argument, at sign photo,
that is the name of the entity.
So remember in the mapping thing when I made a photo entity
and I made a photographer entity,
this is a string which is the name.
So here I am making a photo.
And what does it return?
It returns NS-managed object star photo, and I told you
that all of the objects in the database are NS-managed objects
or subclasses thereof.
And, in fact, that's exactly what happened.
So this is just going to make one for you and return it.
It's going to be blank, or empty.
Now, that could mean that all of its attributes are nil,
but we didn't see this in Xcode, but it,
when you inspect a property in Xcode,
you can actually specify a default value,
and it'll have that value.
So maybe you want it to have the upload date
by default be the date the object was created or something,
you can go in and create [pause] some fixed date
or you want the title to always be at least the empty string
or something like that, you can set defaults,
they have to be constants basically, in Xcode, to do that
and NS object will come back with those defaults set
in those fields, and otherwise nil, okay?
But if you don't do any of that default setting,
all the properties in this photo will be nil.
So the title will be nil, subtitle will be nil,
the who took will be nil, it'll all be nil, okay?
So, great, now I have an object, how do I set those attributes?
I want to set the title.
I want to set the subtitle, I even want to set who took?
How do I do that?
You do that using the key value coding protocol.
And you've already used this protocol actually.
It is value for key, set value for key, value for key path,
and set value for key path.
Okay? So this protocol, which is implemented by NS dictionary,
for example, that's how we used value for key path
in those NS dictionaries, to do the things like description dot
under bar contents, remember that, from Flickr?
This is how NS-managed object works.
It implements all these and you can say value for key,
at sign quote title, and you can get the title of a photo.
Or you can say set value, whatever you want,
for key at sign quote title, and set the value
of a title, simple as that.
Okay? Value for key, set value for key.
Any questions about that?
Okay. So, the, the key is obviously just a string,
the name of the property in your visual map
that we built earlier.
The value would be just an object, like an NS string,
if it was a title, and NS date if it was the upload date
and NS data if it was the thumbnail UR,
thumbnail data or whatever.
You can also set, string set value
for key, the relationships.
So if you had a photographer, which maybe you inserted
with that entity description thing,
so now you have photographer, as well, you can just say the,
the photo set value, quote who took, set the value,
the photographer, for the key quote who took.
Alright? Now, what' really interesting is that if you set
that value who took, the photo set, in the photographer,
will automatically be updated.
You do not have to set both sides of the relationship.
And vice versa.
If I have a photographer and I add a photo to its photos set,
it'll automatically set who took.
Okay? And that's because this database has
to maintain self internal consistency
and so core data does that all for you, which is really cool?
Okay? So you can set either side
and it'll automatically set the other side.
As I said before, the too many ones are NS sets,
and the non-too many ones are just NS-managed object stars.
Okay? Now, all these changes that you're making,
inserting objects, setting all those properties,
those only happen in memory.
They're not happening on disc, until the context is saved.
Now the context has a save method.
Okay? If you go look in NS-managed object context,
it has a save method, but you're not going to call that,
because you're going
to let UI-managed documents auto save, save the context.
Alright? So if you save the document
or you can actually send a message
to the context to save itself.
Either way, it will save out to disc, but until then,
all the changes you're making are just in memory.
Okay? Now, core data has an incredible undo
and redo built into it.
To, you make some changes, you say undo, it undoes them, okay?
And you can batch them up and you can undo things as a group
and can undo just the last chain, I can't,
I don't have time to talk about any of that,
but it's really, really cool, okay?
So anytime you have a situation where you're changing database
and the user wants to be able to maybe undo,
core data's awesome for that.
[Pause] Just like you get UI,
or NS-managed object did save notifications,
you can also get UI-managed document did save notification.
So you can sign up for that radio broadcast and find
out when the whole document did save.
Kind of six, one-half dozen in the other in this case,
because if the documents get saved,
then definitely the context is going to be saved, as well.
Okay. [Pause] Calling value for key and set value for a key
like this though, it kind of results in some ugly code.
Because it's not type checked, right, value for key
and set value for key, that should be set value colon
for key colon, that just takes ID.
Right? Returns ID, so it's really not type checking any
of that stuff, and also, you end up with a literal strings,
like at sign quote thumbnail URL and if you ever changed the name
of that in your visual map,
all your code would just stop working silently
and you wouldn't know, so,
really what we want here is properties.
We want to be to set the title and subtitle and thumbnail URL
and all that stuff in my photo using properties
and dot notation.
So, all we need to do to do that is create subclasses for photo,
and a subclass for photographer.
Subclasses of NS-managed object, right?
And the subclasses will implement all these properties,
and, of course, because Xcode is so nice, it's going to do
that for you, so let's talk about now how
in Xcode we can make it generate subclasses
for photo and photographer.
So I'm going back to my visual map, I'm going to select photo
and photographer, and then I'm going to, from the editor menu,
I'm going to pick this all important menu item called
create NS-managed object subclass, okay?
Because that's exactly what's it going to do.
And when I do that, it's going to say okay,
which of your models, okay, we only have one,
model dot xc data model d, but you could have multiple,
which of your models do you want
to generate managed object subclasses for?
So we'll just pick model, and then, it says okay, well,
which of the classes, which of the entities you want
to create a subclass for?
We'll pick them both, both photo and photographer here, and,
it's going to say where do you want to store them,
we'll store then where we store everything else, and then,
bingo, photo dot m and h, and photographer dot m and h,
you see those there, that got created for us?
So, those are subclasses of NS-managed object,
and when you insert an object in the database, or get one back
on a fetch, which we haven't talked about,
if you insert an object in the database, when it comes back,
instead of being an NS-managed object star,
it's going to be a photo star, or a photographer star, okay?
It's just going to automatically work, that insert entity for,
for entity for name method,
which I can never remember the name of, that will return,
if there's a subclass to be had, the subclass.
Alright, so let's look at the code of this,
let's look at photo dot h, for example, so you can see,
that photo dot h has made an at sign property for all
of my database properties for photos, see title, photo URL,
they're the right type, NS date, NS data,
even who took is the right class.
You see who took is a photographer star.
Now, sometimes you'll just, do this
and who took will still be an NS-managed object star.
Why does that happen?
That's because this is a one-pass generation,
and if it happens to generate photo before it generated
photographer, it won't know about photographer
and it won't be able to do this.
If that happens, just go back to your map
and generate them again, and this time they'll both exist,
beforehand, and so it'll, it'll make the right thing, okay?
So you can regenerate as many times as you want, and, in fact,
we're going to be regenerating as we change scheme,
add more entities, add more properties,
we're going to be redoing that regenerate all the time.
Question?
[ Inaudible Background Question ]
Yes. So, let's, the question is tell me about that
at sign class photographer towards the top there.
Why is that not pound sign import photographer dot h, well,
in objective c, if all you want to be able to do is declare
that something is of a certain type,
and you don't need all the methods, you're not going
to call any of the methods or anything like that,
then you can just do this at sign class directive,
you can do the same thing for protocols,
you can say at sign protocol, whatever, and just declare
that protocol exists, so I don't, I'm not going to call any
of the methods in it or implement any of the methods.
So it's kind of a forward declaration
because eventually anyone who's going to use this is going
to start calling photographer methods and they're going
to import it, but this is just a way to suppress
that compiler warning,
so it doesn't say unknown class photographer, without having
to import photographer [inaudible].
That kind of keeps them independent,
that way you wouldn't have to generate photographer dot h
if you didn't want to, you just wanted photo and then
that could be, although, that wouldn't be true
because then it would be NS-managed project, but anyway,
if you have NS-managed object, there,
then just generate them again.
Alright, so let's look at photographer dot h,
you can see it has name and photos, right, as promised.
Photos is an NS set, you see that?
It also gave me a whole bunch of methods here for adding photos
to the photo set, because that NS set,
that's an NS immutable set, so if I wanted to set the list
of photos, I'd have to create a mutable set,
put all the photo objects that I wanted in there,
and then set the whole thing.
You know, say, you know,
my photographer dot photos equals a whole set.
With add photos object, or remove photos object,
I can just add one photo at a time.
Right? So it's just kind of, those are convenience methods
for adding photos to that photoset.
So let's look at the dot m's of these though.
Okay? And what do you imagine these dot m's look like?
Like set setters and getters?
Or, at sign synthesizes or something like that?
And the answer is none of that, okay?
The implementations of these just say at sign dynamic
for all of the properties.
Now, what the heck is at sign dynamic,
we never seen that before?
At sign dynamic basically means hey I know what I'm doing,
don't generate a warning for this, okay?
And what does NS-managed object do, in this case,
since there's no at sign synthesize, there's no setters
and getters here,
the implantation just suppresses the warning, basically,
and the answer is objective C, the run time,
has a trapping mechanism where if you send a message
to an object, and it doesn't understand that method, right,
it doesn't implement that, it can trap.
And go try to figure out something else to do.
Okay? Without crashing or saying does not respond to selector.
And, NS-managed object, when it gets sent a message it doesn't
understand, it tries to do value for key,
or set value for key on it.
Alright? And if that doesn't work,
then it says does not recognize selector,
but if it does work, then it just works.
Okay? So that's what's going on here, NS-managed object traps
when setters and getters or any method is sent to it
and it tries to do value for key and set value for key,
and if it can, all is good, and if it can't, then it says,
does not understand selected.
Everyone understand that?
So this at sign dynamic just says don't generate a warning
that I don't implement the setter and getter,
because I know what I'm doing.
Okay? Alright.
So, so now that I have these subclasses, how do I use them
to access my attributes using dot notation,
and so now when I say NS entity description, insert new object
for entity for name, in managed object context,
instead of saying NS-managed object star photo equals that,
I say photo star photo equals that, insert a new object
for entity for name, by the way, returns an ID,
so the complier is not going to warn you
or give an error in either case.
But if you say photo star photo equals that,
then you can just say photo dot title equals,
whatever you want the title to be,
like if I'm downloading this Flickr,
I might say Flickr data object for key Flickr photo title,
get it out of that dictionary that came from Flickr
and I'm just setting that title in my database.
Okay? Exactly 100 percent what you would think it would be.
Here's a whole bunch of other examples of what you would do,
you can also obviously call the getter,
so I could NS string star my thumbnail equals photo dot
thumbnail URL, okay?
Get that thumbnail URL out of there, that,
notice that you can't put URL's in the database,
you have to put strings and convert them to URL's.
I could say photo dot last view date equals NS date date, okay,
the date method in NS date gives the current date and time,
I could set that as the photo's last viewed date.
I can even, I can obviously do photo dot
who took equals some photographer object,
but I can even say photo dot
who took dot name equals the name of the photographer.
So if I have a photo, a photo star,
I can actually set the name
of the photographer who took that photo.
Okay? So I'm just using dot notations
and normal dot notation,
it's just that photographer implements name
and photo implements who took and who took is a photographer,
therefore, photo dot who took dot name.
Question?
[ Inaudible Background Question ]
Alright, so the question is would it auto create, if,
if photo dot who took was nil,
would it automatically create a photographer in the database,
so that it can say dot name equals whatever.
And the answer is no.
This is normal properties.
If photo dot who took is nil,
then the set name will just be sent to nil, it will do nothing.
So the only way to get things in the database is insert
for entity for name, the thing we saw above there,
insert new object for entity for name, okay,
it doesn't auto create the [inaudible].
Okay. So that's how I access my attributes.
What if I, though, wanted to add code to my photo
or my photographer classes?
I could put that code in photo dot m and h,
but that would be a problem.
And why is that?
Well, that might be a problem because, well, first of all,
why would I want to do that?
Let's say I wanted to add a class method to photo
that took a Flickr database, a Flickr dictionary as an argument
and created a photo in, in the database,
that would be an awfully convenient utility method
to have in photo, right, I could open square bracket photo,
make a photo with this Flickr data, in a context,
and it could do it, right, so that would be cool.
Or if I want to derive a property, like,
I've got this nice property thumbnail URL,
but unfortunately it's a string, what if I wanted
to have a UI image which was the thumbnail UI image,
then I could have a little method in photo
that just self dot thumbnail URL, turn it into URL,
go look it up somewhere, preferably not on the internet,
but maybe, maybe it would just be a blocking,
a method that would block, I don't know.
So that would be cool to add to photo, as well,
so in other words, it would be nice
if we could put all our photo-related stuff
in photo dot m and h, not just the setters and getters
for all our properties.
Why this is a problem though, the problem is
as you change your visual map, you're constantly calling
in that regenerate method, you know, menu item,
the create NS-managed object,
so it's called always rewriting them, okay,
it's always blasting what's in there, so we can't do it.
Okay? If we edit photo m and h then we can no longer go
into Xcode and say create managed object subclasses
for me anymore, and that's a bummer.
Because, especially in development, we're iterating,
we're adding some properties, we're adding some entities,
changing some relationships, we want to constantly be going back
and regenerating those things, okay?
Question?
[ Inaudible Background Question ]
Yeah, so, the question is could I set the default for the
who took property to be photographer alloc init,
basically, so the, the default would always default
to creating a photographer
and unfortunately you can't do that, but that'd be cool.
The defaults all have to be kind
of like I say, static, [inaudible].
Alright, so, what are we going to do then, because we want
to add methods to photo dot m and h,
and we want to add methods to photographer at m and h,
but we don't want to have to touch those files.
Well, we're going to use a new objective C language feature.
The last one I'm going to teach you, okay?
I think you know them all after this, called categories.
Okay? Categories, let's you add a method to a class
without sub classing it.
Okay? And, you don't even have to have the source
for that class that you're adding the method to.
Okay? So what are some of examples of this?
Well, in the UI kit, there's a method NS attributed string,
draw at point, okay, well NS attribute a string is
in foundation, it's not a UI kit thing,
it's just a generic attributes on a string thing,
but UI kit adds all these, you know, draw at point,
and it defines all these other attributes that can be on there,
and it does all that in UI kit,
and it doesn't even need the source for NS attributed string.
Okay? It just needs to know that class exists.
Same thing, NS index path.
I told you NS index path only had two methods on it,
row and section, but actually NS index path has a whole bunch
of methods on it, it's in foundation,
it's just a generic index pointer into an arbitrary,
you know, linked list of items,
it's just that UI table view wants to call it row
and section, so it adds the property's row and section
to NS index path, even though, again, table view is in UI kit,
NS index path is in foundation,
not even the same framework, okay?
So, how does this work?
And it looks like this.
You have an interface and implementation,
just like the class, but you say, instead of saying
at sign interface, name of class, colon, superclass,
you say at sign interface, the name of the class I want
to add methods to, and then in parentheses, what I'm going
to call this category, and you can call it anything you want.
SO here I've called it add on, but you could call it Flickr,
you could call it Create, whatever you wanted to do.
And then you just list all the methods you want to add
to this class, and they can be properties,
here I have a read-only property and another method.
You just add, put them on there, and then at sign end.
Okay? So that's all you need to do.
Now anyone who wants to call these methods just has
to import this header file that has this in there.
Now, there's a big restriction on categories,
gigantic restriction, you can't use any instance variables.
Okay? So the implementation
of your methods cannot use an instance variables,
or any stored data at all.
Okay? There are ways kind of around it,
but they're hacks, okay?
So generally, categories is four methods, they have no state,
well what good is that?
Well, the object that you're adding the methods to,
it has a lot of state
and so these methods would be all implemented
by using the state of the object you're adding the methods to.
Okay? So, like let's at an example for these two methods.
So here's the implementation, the at sign implementation
of photo, photos add on category,
so I'm adding these two methods to the photo class,
which is that thing we generated in Xcode, image,
you see it just uses self dot photo URL, self, is the photo,
because these methods are being added to photo class.
So, self is a photo, dot photo URL gets
that attribute, gets that, that data.
That's probably, it actually probably would be self,
it would NS URL from string, self dot photo URL string,
because we can't put URL's directly in the database,
but anyway, and then it would say return UI image,
image with data, and return the image.
Okay? So no instance variables there,
it's not using any [inaudible], same thing with is old,
it might say self dot upload date, time interval
since now is greater than a day ago.
Okay? Question?
[ Inaudible Background Question ]
No I mean you cannot declare any new ones.
Okay? So you cannot use any instance variables here,
you could only use instance variables in the class,
you're adding a two if they make them public,
via properties or whatever.
[ Inaudible Background Question ]
Okay. You can't have any instance variables
in a category, period.
Okay, you cannot declare any.
If you want to use instance variables
of the class you're adding the methods to, right?
Then they have to be public
because all you can do is see the public header file,
you don't see the implementation of photo,
you're adding these methods to it blind,
you don't know anything about its implementation.
So anyway, so this is called categories, I don't teach this
to you early on because this is easily abused, alright?
You could add methods to UI kit classes,
there's no reason you couldn't.
You could add methods to, you know, foundation classes,
completely unrestricted.
The only thing is when you start doing that, you're code can kind
of get a little obfuscated, people are like what,
I didn't know that, you know, this UI kit class had
that method, and it's because you've added it as a category
over here and so it's just kind of hard to understand,
but sometimes like this, we want to do that because it kind
of it, it collects our code and it makes things look nicer.
Question?
[ Inaudible Background Question ]
So the question is can I override or overwrite, really,
a method that already exists?
And the answer is don't do that.
Okay? That is really hard to understand
for people reading your code, what you mean by that.
So only add methods, don't try to replace or otherwise,
you know, use the same method.
That's a good question.
Alright, so, here's a common category method we add
for subclasses of NS-managed object, which is creation.
So, imagine I had this category called photo create,
and I'm going to have this method that I'm going to add
to photo called photo with Flickr data
in managed object context, and it's going to query
that database, see if that photo already exists,
it's going to return it if it does, if not,
it's going to insert new object for entity for name,
initialize the photo from the dictionary you sent,
etc. etc. etc. It's going to do all the work, all in one method.
So that if someone wants
to create a new photo using Flickr data,
they can just call this one method, class method in photo.
Okay? So this is a classic reason why we would do this.
Okay? So, how do we create a category.
So we go to objective C, new file, of course,
and instead of saying objective C class, right,
you see objective C class there, you say objective C category,
and instead of asking you the class and the super class,
it's going to say what class do you want to add a method to
and what do you want to call this category.
So I'm going to add methods to photo, and I'm going
to call the category Flickr.
And then it creates photo plus Flickr dot h
and photo plus Flickr dot m. Now, this is kind
of the standard naming convention, for a category.
You call it name of the class you're adding the methods
to plus the name of the category.
Okay, the plus as a separator,
and also because plus means you're adding these methods
to it.
So it's kind of a naming convention.
You could call those header file
and the implementation file anything you want, really,
but this is what we one hundred percent of the time call it.
Okay? Name of class, plus category.
And you can see that it's created a dot h for me
and a dot m, and I just put the methods I want to add
in the dot h and then I implement them in the dot m,
it's just that I can't, unfortunately,
have any instance variables.
[Pause] Okay?
Yeah, question.
[ Inaudible Background Question ]
Yeah, so the question is instead of using new file, category,
if I just created an empty file
and started typing this stuff in,
how does objective C know this is a category, well, the,
when you go new file in category,
that's just a convenience, it's just creating this
for you as a convenience.
You could certainly just type this stuff in,
it knows it's a category because it's at sign interface photo,
parentheses Flickr, right,
it knows that that parentheses thing,
if you have at sign parentheses with some word in there,
it knows that means a category, and then same thing
on the implementation side, you say okay,
I'm providing implementation of that category.
So the answer is yeah, you can do that,
new file is just making it easy for you
so it does this, that's all.
Okay, any question about this?
And we'll see this in the demo on Wednesday
where it will add a category, to photo.
Okay. So now you know how to insert an object, you know how
to change their properties.
We know how to create the custom subclass, so we can do all this
with dot notation, what about deleting objects
from the database, okay?
This is all too easy, okay,
as Darth Vader would say, all too easy.
You just call delete object on the context,
give the object, bam, it deletes it.
Now, deleting the object can have ramifications.
Right? You delete a photographer, it's going to set
that who took to nil, okay?
Assuming you don't do that cascade delete rule
in which case it would delete the photographer, but, okay,
so deleting, you know, has ramifications,
you got to know what you're doing
and deleting, it's very easy to do.
Two things to think about this.
One, remember it doesn't actually delete the photo
or delete the object until you save, so the auto save comes
around and it'll be deleted.
That's just a minor point, it's kind of obvious.
Same thing with all the changes you make, that they don't happen
on the database until that auto save happens,
or until you explicitly save.
But the second thing with deletion,
you got to be really careful is, see that argument photo?
Delete object colon photo?
If that photo is a strongly-held two pointer, you better set it
to nil right after this.
Okay? Because you cannot access
that photo anymore, it is invalid.
Right? After you hit delete photo, or delete object rather,
that thing you deleted, get rid of all your strong pointers
to it, because it's no good.
Right? It's been deleted.
So if you tried to say photo dot title equals something
on the next line, I don't even know what would happen,
something bad, don't do it.
Okay? I mean, that's completely obvious, right?
But, it's something people forget and then they're like,
wow, it's not working,.
Okay? One of the things about deletion that's kind of cool.
Core data will send the method, send a message,
prepare for deletion to all objects
when you send delete object.
Okay? So if I say delete object photo, that photo will be sent,
if it implements it, prepare for deletion.
And the cool thing is, you can put this prepare
for deletion in a category.
So you can add the prepare for deletion method to photo,
or photographer, in a category.
Now what would you do in that prepare for deletion, well,
you don't need to do anything with the who took or the photos
or anything that, that's all taken care of you for you,
but what if you had another property somewhere
in the database, which was counting the photos,
or something like that, and deleting this photo would change
that count, and why would you have such a thing?
Because it's possible to just query the counted photos,
that a photographer has, for example.
Maybe, you know, it's your, its account, for efficiency,
you're checking something about the photo
and keeping this count.
And maybe if you delete the photo you need
to update that count.
Well you can do that, all that kind of stuff here
and prepare for deletion.
Okay? You can basically do anything
in the database you want to prepare, prepare for deletion,
and the thing that's being deleted is not yet deleted.
Okay? So it's still it the database that this is called.
But after you return from this method, then the object's going
to be deleted, so you have better have updated the database
however you need.
Okay? Got that?
Alright. So, now you can create objects with insert,
you can get inside the properties,
you can delete the objects.
One last thing to want to do in a database, query, okay,
which means go out into the database and get me objects
that match some criteria.
Okay? So, we do this, in core data,
with an object called NS fetch request.
Alright? It's exactly what you think it is, you're requesting
to fetch some objects from the database.
You need four things to make an NS fetch request.
The entity to fetch, and this is an interesting one
because when you fetch from the database, you can only fetch,
fetch, when you execute it, it returns an array of objects,
an array of NS-managed objects,
or subclasses thereof, as you might imagine.
That array all have to be the same type of entity.
Okay? So there are no way to do a fetch in a database
that gives you an array back, some of the things are photos
and some are photographers.
They all have to be photos, all have to be photographers.
So you have to specify the entity
that you're trying to fetch, okay?
Understand what I mean by that?
That's the most important thing, in fact, the,
the way you create the fetch request is
by specifying what kind of entity you're trying to fetch.
Then you can specify, optionally,
how many of those objects you're willing to accept
and how big a batch, if, if you're fetching a whole bunch
of them, you can specify how many to get at a time,
because it's not going to, if you say get me these photos
and it matches 10,000, it's not going to load 10,000 things
out of the database and into memory.
Okay? It's going to kind of give you 10,000 placeholders
and then it's going to fetch them in little batches,
and you can specify the batch size depending
on how you're going to use the object,
if you use the information, if you're displaying it
in a table view, well you can set the batch size real small,
like 10 or 20, however many, you know, would scroll
around on a table view.
We can only see, you know, 10 or 20 at a time,
so you've got a small batch size.
So you control that.
Sort descriptors, which we're going to talk about,
which is how to sort the results,
because the results are an array, not a set.
So the array means it's ordered, so you got to specify what order
to give those objects back in.
And then, most importantly, the predicate,
and the predicate says which of those entities you want,
which photos?
Which photographers?
Okay? So let's look at these four things.
Here's what a fetch request looks like.
You create it with fetch request with entity name,
that tells you what you're fetching out of there,
and then you can set things like the batch size
and the batch limit, they're just properties on the request,
and then the sort descriptors is an array of sort descriptors,
we'll talk about why that's an array.
And then the predicate is, is NS predicate.
So this is how you would set up a fetch request.
Okay? Very simple.
So, you can look up in NS fetch request how batch size works,
how fetch limits works, I'm not going to get into detail
about that for time reasons, but I am going to focus
on sort descriptors and predicate
because those are really important, so let's look
at the sort descriptor.
You create a sort descriptor using this class NS
sort descriptor.
And it has quite a number of class methods to create it,
so you should go look at the documentation
and familiarize yourself, but they're generally
of the form sort descriptor with key, so that's the key you want
to sort by, so if I were fetching for photos,
I want to get the result sorted by the title of the photo,
that would be a classic thing.
Ascending is whether it's alphabetical order
or reverse alphabetical order,
and then the selector is interesting there.
That is the method that will be used to compare photos,
while it's sorting them.
Okay? So, it's going to sort the results by the title,
and it's going to be using the method here,
localized standard compare to compare the titles
to know how to sort them, okay?
Now, A, what is localized standard compare?
Localized standard compare means sort them like they would look
in the Mac finder, basically.
Or, that's kind of a local way of saying it, but basically,
sort them how real people think things should be sorted,
not case insensitively, do the right thing
with diacritic marks, if it's a language that has a lot
of diacritics, that kind of thing, okay?
That's what localized standard compare is.
There are other selectors that are sensible
if it was a string type property,
like localize case insensitive compare, which sounds the same,
case insensitive sorting,
but the diacritics are handled differently in that case.
So that's why you would want to use standard compare.
So you can look up in sort descriptors, see what kind
of methods are kind of built in.
If you were, you can sort by things besides strings,
you can say sort descriptor with key upload date,
and then the selector would want to be compare colon.
Compare colon is a method most things, if not all,
except for NS data maybe, things in the database will respond to,
compare colon, it's kind of the default compare,
but it's not going to do what you want,
usually with reverse string, so you want a better one,
but it will work for dates and things like,
and numbers, things like that.
So that's the selector.
You can leave that selector off,
when you create the sort descriptor,
just say sort descriptor with key ascending,
close square bracket, and you'll get compare, okay?
So, compare is kind of the default one you get.
Why do we give an array of these things to the fetch request?
Okay, when, we saw request dot sort descriptors,
it was an array of these,
well that's the old last name, first name thing.
If I said hey, I'm going to fetch some people,
some employees out of an employee database,
and I want you to sort it by last name.
Well, of course, all the Smith's would all sort
to the same thing.
So, you want to then sort those by the first name.
So you provide sort descriptors, one that sorts by last name,
that's the primary sorting, and then one
by first name is the secondary sorting.
Another common one is a table view, pay attention,
because your homework, you might have to do this,
is maybe I'm doing the table view
like you're doing your current assignment, and I'm sorting,
you know, basically by the name of the place, in the place one,
but I'm supposed to do it by countries, sections by country,
so really I need to sort first by country,
then I sort by the name of the place in the country.
Okay? So, that's an example
where you might use two sort descriptors.
Country sort then the place.
That's so that I can get them in order so that the,
their by country first.
Okay? That make sense?
Alright, predicates, so this is the guts, this is the thing
that says which photos do I want,
which photographers do I want, and,
and this predicate looks a lot like NS string
when it comes to creating them.
So just like we have NS string, string with format,
we actually have NS predicate, predicate with format, okay?
And you can specify this arbitrary string.
Now, this is good and bad.
The good news is this is incredibly flexible
and you can specify unbelievable kinds of [pause] criteria
for finding what things you want in the database.
The bad thing is, it's a little bit for you to learn,
because it's almost like you have to learn a little bit
of a language here, the predicate language
for how you specify what you want to choose.
The basic format [pause] allows you to do the percent
at sign replacement, just like you could with NS string,
so here, for example,
I'm looking for all the thumbnail URLs that come off
of Flickr five server.
So I'm saying predicate is thumbnail URL contains Flickr
five, but I don't put the Flickr five right in there,
I use percent at sign, and it's an argument.
So I can use percent at sign for replacement,
and I can replace not only the right side, what's contained,
but I can replace the left side, like which property I want
to search to see if it contains Flickr five, okay?
And we'll talk about contains in a second, question?
[ Inaudible Background Question ]
Correct. Yeah so, this is not just, the question is
that just making a string and passing it off
and the answer is no, NS predicate is actually looking
at what you're par seeing and, now it's,
it can't know what database you're doing a fetch into,
so it won't be until fetch time that it might fail.
If you say thumbnail foo, contains whatever,
its predicate is going to let you create that predicate,
but then when you go to do the fetch it's going
to say no property thumbnail foo in this database.
Okay, so, you see this one's contained.
Contains means, you know, that string is contained
in the other string, exactly what you want,
so contains is one of the words in this little language
for setting up predicates.
Here's a whole bunch of other examples.
I'm not going to teach you all of predicate today, you're going
to have to look at NS predicate documentation,
see what it can do, but here's a bunch of examples.
Obviously, you have equals,
so you can say unique ID equals percent at sign,
maybe the Flickr object, the Flickr photos, unique ID,
you can say name contains with square bracket C,
that means contains case insensitively.
Okay? So that's part of the, you can have greater than
and less than, so if I had a, a date property called viewed,
I could say if viewed is greater than a certain date,
like today's date, for example, or, it would be, I guess,
less than today's date or less
than today's date minus 24 hours, whatever,
so you can use greater than or less than.
You can use dot notation to follow relationships.
So you can say who took dot name equals CS193p instructor, right?
So you could have, this would be a query or a predicate
for searching in the photos, entities, so you're trying
to get photos entities, but you want photos entities where the
who took name is some photographers name.
So, the things you're getting are photos, but the query,
the predicate you're using to find them,
is looking over at the photographer using dot notation,
you understand this?
Okay? You definitely need
to understand this for your homework.
And you can do really powerful searches
like give me any photo who's title contains, sorry, give me,
this would be a photographer query,
so let's say I'm searching photographers,
give me any photographer where any
of its photos title contains this.
Okay? So that any is kind of a special thing,
and that means I'm the photographer, look at all my,
look at, in the database at all the photographers,
look through their, their photos set,
to look at all their photos, look at all the titles
of all those photos, see if it contains this string,
and if it does, return that photographer.
Okay? Now this seems, I'm sure you're like whoa, that is going
to take a lot of computing power if I had thousands of photos
and hundreds of photographers, but actually, databases know how
to do this really, really well.
Okay? And all this stuff that you're doing is generating stuff
that happens on the sequel side, so you're going
to get a big sequel statement generated here
to join these tables, do these searches,
it's going to be incredibly efficient, and one thing
to understand about core data is it's very efficient, okay?
The sequel it generates is totally tuned to the max.
Okay? Having said that, there are certain queries
that are more efficient than others
that can accomplish the same goals.
So, if you get to be a core data expert
and you start building big databases
and you're making a lot of big queries on big data sets,
you will eventually learn what makes a good query,
what makes a not so good query, etc. For your homework,
you don't have to worry about that yet.
Okay? Let's get the basics first.
[ Inaudible Background Question ]
So the question, you mean like the greater than and less than,
can I pass that in as a percent sign?
[ Inaudible Background Comment ]
Yes. You can't do sequel,
the question is can I do sequel injection by using the percent
at sign to, to basically make the operator be, you know,
configurable, and the answer is you can't do that.
NS predicate would complain about that.
NS predicate does know when a percent at sign is a right-hand
or a left-hand of one of these expressions,
so the answer is no.
You can't do that.
Okay, so there's a bunch of examples, again, I'm just trying
to give you the examples, we're not, we're not trying
to teach you predicate here.
There's also a compound predicate.
In the NS predicate, you can say and or or.
So you could say name equals percent at sign
or title equals percent at sign, okay, if you had an object
that had a name and a title.
Or you could say who took dot name equals blah,
or title equals blah, it kind
of wouldn't make much sense, but you could.
You can also create compound predicates in code by doing,
using and predicate or or an predicate
in NS compound predicate.
And it just takes an array of other predicates
and makes a compound one, okay?
That's like if you want the and-ing and or-ing to kind of be
if then, okay, in code,
as opposed to just built into the string.
Advanced querying, for time reasons I'm going to skip this,
but key value coding, which is the thing
that lets you use dot notation to search down inside
of a dictionary, right, it's the value for key path business.
You can kind of do value for key path in the database,
and there is, we didn't talk about this,
but there is a really cool thing in key value coding,
which is functions, and one of the functions,
which you probably will need for your homework,
so pay attention, is at sign count.
So, if you put at sign count in the thing you're querying for,
as long as the thing to the left of it is an array, or a set,
sorry, a set, one of these NS sets, then it will replace
that with the count of how many things are in that set.
So, for example, if I wanted to find out all the photographers,
give me back all of the photographers
who have taken more than five photos,
my predicate would be photos dot
at sign count is greater than five.
Okay? And there are other at signs, at sign average, at sign,
you can look through and find out what there is, I put a link
in here, you know I don't, I usually don't put links
in my slides, but this one is worth looking at.
And, this also works, it works for any key value coding things,
like for dictionaries, so go back to the shutter bug
and try doing property list results, value for key path,
photos dot photo dot average dot latitude.
Anyone hazard what that returns?
[Pause] Yeah?
[ Inaudible Background Comment ]
Correct. It returns the average latitude
of all the photos, okay?
So, that's something for you to, to have fun with.
You can build even more complicated expressions
and there's a mechanism to query into the database
and not get back an array of NS-managed objects,
but to actually get, you know,
kind of data that's been calculated from,
from what's in there, and when you do that,
instead of getting an array of NS-managed objects,
you get an array of NS dictionaries,
and those dictionaries contain keys and values,
which is the data you look for.
This is all super advanced, see the title of this thing,
advanced querying, not going to talk about it in this class,
but just so you know, that fetch request can be used
to create something that's actually fetching data rather
than just fetching managed objects, okay?
Alright. So, let's put it all together for the request.
So I created, this is a request, okay?
That's going to get all the photographers,
so I say fetch request [mumbling] photographer,
who have taken a photo in the last 24 hours,
so I get yesterday, which is NS date, date with time interval
since now minus 24 hours.
Predicate, any photos dot upload date greater
than whatever, okay?
Yesterday.
And then I'm going to sort it by the photographer's name,
so an array of one sort descriptor,
where the sort descriptor's the name,
it's going to be using compare here,
which is probably not good, I probably want to say selector
at sign localized compare.
Okay? So that's I would create a request.
Now that I have a request, how do I execute it?
And the answer is we use the method execute fetch request
in NS-managed object context.
So I told you NS manage object context is the hub of everything
and it's the hub of querying, as well.
And you can see it has a little extra argument there,
error colon, at sign error, which will return an NS error
and tell you what went wrong if things went wrong,
and you can tell things went wrong with the fetch request
if execute fetch request returns nil.
Okay? If execute fetch request returns nil,
something went wrong, and that
at sign error will be filled out with some error.
If it returns an empty array, [pause] that's not nil,
empty array, that means nothing matches what you're requesting.
So no error.
It's just that you requested objects and there aren't any
that match that predicate.
Okay? Otherwise, it's going to return an array
of NS-managed objects.
Okay? Or, subclasses thereof, photos stars,
photographer stars, whatever you set as the entity name,
it's going to pass an array of them.
Okay? That's it.
It could not be simpler to query.
One thing about the query results, by the way,
if you query 10,000 things, you're not going
to get 10,000 things, you're going to get 10,000 placeholders
and as you start looking at the attributes,
then it'll start faulting them in from the database.
Okay? So there's a lot of performance optimization going
on behind the scenes that you don't really need to know about,
but faulting is happening, for those of you
who are database heads and you're probably worried
about this, no worries, it's not actually pulling all that data
out of those tables, it faults them in as you access them.
Quick thing about core data thread safety.
NS-managed object is not thread safe, in other words,
you can't just use it in multiple threads,
but you can safely access them.
Most managed objects, we create them with this kind
of thread containment, thread concurrency mechanism.
There's a method in NS-managed object context,
which you will want to use, called perform block,
it just takes a block with no arguments.
Anything you want to do on a context, inserting objects,
querying, anything, do it inside a perform block.
Why do you do that?
It'll do it on the safe queue for that context,
and it'll guaranteed to be safe, thread safe.
Now, that safe queue might be the main queue,
so this will not necessarily give you multithreaded, okay?
But it will be safe, so get in the habit of doing this,
because if you do come, come to a day
when you're using NS-managed object context
and you're creating them on different queues,
so you can load the database in one queue while you look
at another, whatever, which is all doable,
you need to be doing this performed block, it doesn't hurt
to do it, so might as well do it.
And parent context I don't have time to talk about.
There is a ton other stuff, actually,
in core data I don't have time to talk about because we're
at the end of lecture today.
Optimistic locking, rolling back unsaved changes to, you know,
states that make sense, undo and redo I talked about.
Staleness, you do a fetch and it sits around for a long time,
the data could be stale, it might need to be refetched,
thrown out and refetched.
It just, a massive amount of stuff, can't talk about it.
When you walk out of here, what you really want
to be comfortable with is you know how
to create the visual map, insert objects, make the subclasses,
use dot notation to access them,
delete objects, and query for them.
Okay, that's the fundamentals of core data, that's what we talked
about today, that's what you should,
need to know how to do for this class.
And then outside of that, you just need to know
that there's a lot more, and if you're going
to do serious database work, you need to some reading up on that.
Okay, so that's it.
Sorry to keep you a couple minutes over and I will see you
on Wednesday with a big demo of all this stuff.
>> For more, please visit us at stanford.edu.