[ 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.
