Transcript
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[Applause]
Good afternoon and
welcome to Session 220.
My name is Rishi Verma and I'll
be joined by Scott Perry later
and we'll be presenting What's
New in Core Data this year.
Before we get into
what's new though,
I'd like to tell you a little
bit about what is Core Data.
Now a lot of you have made these
amazing apps with beautiful UI
and you've tied it all with
data that you're either getting
from an external data
source or provided
with your resource bundle.
Now as you process those
objects you're building
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now as you process those
objects you're building
up a complex graph and shuttling
all those changes to your UI.
And then as your
user makes changes
on the UI you're pushing
all those changes back
through your object graph
and back to your data source.
Well, Core Data makes this easy.
Core Data will manage
your object graph for you.
Simply tell us a bit about
your Cocoa model there
and the object model
editor, tell us a little
about your objects,
their attributes,
how they're all related to
each other and we'll take care
of the rest and we'll also
persist it in the back end
of your choice, be it sequel
light or your own custom store.
Now, as you ingest objects, your
relationships will be changing
and Core Data will maintain
these for you, so if you set
up any delete propagation rules
in your object model
we'll delete and object
and propagate those
deletes as you define.
Finding objects in your object
graph is also particularly easy.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Finding objects in your object
graph is also particularly easy.
Simply use an NSFetchRequest
and give it a predicate
to find the objects
you're looking for.
And we'll go and
find them for you.
Also convenient is batching.
This allows you to only pull up
a smaller portion of the objects
in your data set that may
result from your fetch request,
allowing you to have smaller
chunks of data as you go
through your data set and
also another candy feature
of NSFetchRequest is
relationship prefetching.
Tell us an object to fetch
and we'll prefetch all
of its related objects
so when you traverse
that relationship you're
still doing so in memory.
Then you simply just
tie this all to UI.
You take an NSFetch result
controller and tie it
to a Table View like
we've done here.
And as I delete the
Apple butter,
my UI will update accordingly.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
my UI will update accordingly.
And then as I ingest more
objects and I add banana bread,
my UI will update
accordingly as well.
This is all handled for you, you
get all the key view behaviors
and change notifications
handled for you by Core Data.
Excuse me.
Now there may be a scenario
where your user is manipulating
the object on the main context
and on a background context
you're ingesting the same new
object and possibly updating
the object the user currently is
also manipulating.
This is introducing the
multiwriter conflict.
In Core Data it has you
taken care of here as well.
We version all the objects and
allow you to set a merge policy.
If you do not set a merge
policy we'll default to error
when you save in your context
and give you a conflict
error allowing you
to address the conflict
as you see fit.
Or you can choose from
several merge policies
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Or you can choose from
several merge policies
that we have already provided,
be it the in store memory
persistent store trumps what's
in memory or what is in memory
trumps the persistent store.
Choose what is best for you
and your particular scenario.
Once you've adopted Core Data
you get several benefits.
I would like to give
you two in particular
that really are the best ones.
An excellent memory scalability
and aggressive lazy loading.
What that means, it is we'll
only load the objects you need
when you need them into memory.
Adopting Core Data leads to
a much smaller footprint,
over 50 to 70% less code for you
to maintain allowing you
more time to go and work
on new features for your app.
Then you can join the
over 400,000 apps already using
Core Data in the App Store.
That's a brief overview
of Core Data.
Now let's jump into
the new stuff.
All the new APIs
we have for you.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
All the new APIs
we have for you.
First, let's start off
with MS manage object
and a new property called hash
persistent changed values.
Previously you may
have used hash changes,
this was a rather basic dirty
flag, if you touch the object,
we would mark it dirty.
But with hash persistent
changed values we'll ensure
that the properties on the
object are different than what's
in the persistent store
ensuring you don't have any
false positives.
Also new on NSManaged
object is object ID's
for relationship named.
This is ideal for working with
large relationships mainly
because we won't materialize
the entire relationship
in memory rather we'll return
to typed array of
object IDs to you.
This allows you to go through
these object IDs and work
with your objects
in smaller sizes.
Let me show you a quick
example of this in code.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Let me show you a quick
example of this in code.
Here I am with my person object
and I ask for its object IDs
for the relationship
named family.
This gives me all my
relations and then I can go
and fetch these relations
in a batch size of 100
and then traverse through
these relationships at 100
at a time keeping my
memory input rather small
and manageable.
Let's move on to
NSManaged object context
and a new method called
refresh all objects.
Refresh all objects does exactly
what you expect it to do.
It refreshes the
objects in your context
but preserves unsaved
changes and unlike reset
on the context your object
references remain valid.
So you don't have to refetch
any references and it is ideal
for breaking retain cycles
which may have occurred
when you traversed a bi
directional relationship
and looped yourself around.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and looped yourself around.
Also new on NSManaged
object context is for those
of you using multiple
coordinators in your store.
Merged changes from remote
context save will take a save
notification from one
coordinator and apply it
to the context in
another coordinator.
This allows you to have
the latest row data
in all your context
and we'll take care
of all the necessary
context for you.
In Core Data occasionally
you'll run
into one particular
exception and that sticks
out a lot to developers.
That's the inability
to load a fault.
Why is Core Data unable
to load this fault?
Well, as I mentioned earlier,
Core Data is aggressive
about lazily loading objects,
you'll only have a portion
of your graph in memory and
it is possible as we try
to traverse a relationship we'll
try to have to go back to disk
and that object has been
deleted out from underneath you.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and that object has been
deleted out from underneath you.
What's better than
throwing in exception,
there is a lot of things.
We have introduced
a new property
on the NSManaged object
context that allows you
to set some basic
faulting delegated.
[Applause]
>> Currently should
delete inaccessible faults,
defaults to yes.
If we encounter a fault we'll
mark the fault as deleted
and any missing attributes
will be null, nil or zero.
This allows your app to
continue on with this object
and treat it as a
deleted object.
No longer will you
crash but you'll be able
to merely keep going on and show
the user what they have expected
to see.
Now on NSPersistent store
coordinator we have two new APIs
to introduce.
We introduced these two new
APIs because we have seen issues
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We introduced these two new
APIs because we have seen issues
with the way that developers
clear up their persistent store.
A few of you have done this.
You have gone through and
bypassed the Core Data API layer
to manipulate your
database directly.
Unfortunately this has some
unexpected consequences,
you may be leaving
bad descriptors open
and so we have supplied you with
destroy persistent store at URL.
[Applause]
>> Like ad persistent store at
URL you take the same options
and you can destroy
that persistent store
and we'll honor all locking
protocols as well as clearing
out all the related files
to the particular store type
that you have chosen to use.
In that same vein we have
introduced replace persistent
tore at URL, similar pattern
as destroy and if the database
at the destination doesn't exist
we'll simply copy it into place.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
at the destination doesn't exist
we'll simply copy it into place.
One of the problems you have
all run into is duplicates.
Having a database with
duplicates is not useful.
You have written a lot of code
to ensure you don't
have duplicates.
Core Data can help
you out here too.
First let's look at a common
pattern you may have used
to find duplicates.
That's the find and
create pattern.
Here as you see, I set up a
fetch request and I had to go
and look for one
particular object to see
if it exists before
I can create it.
If it does exist, I update it.
Well this pattern can be rather
racey and it can also lead
to more duplicates if I have
several threads ingested
from multiple data sources.
Well Core Data has you covered
this year, simply tell us
which attributes should be
unique across any entity
and we'll make sure all
instances of that entity keep
that unique attribute, be it
email addresses, part numbers,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that unique attribute, be it
email addresses, part numbers,
UPC, you name it, we'll
make sure it is unique
across all instances.
[Applause]
Unique instances, unique
constraints are best used
when your on values
that are unmodified
after object creation, generally
when you create the object these
unique constraints should be set
once and then never changed
to the life of the object.
Changing them could
result in conflicts
as your unique properties may
collide with another object
that has the same
unique attributes.
That's where you can
use the recovery methods
in the merge policies we
talked about previously
to address those issues.
Also any of your entities
that inherit from a parent
that has unique constraints will
inherit those unique constraints
as well.
In this example above you
can see the parent has UUID
constraint identified
as a unique constraint.
The sub entity has added
email as an addition
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The sub entity has added
email as an addition
to its unique constraints.
Now I would like to take a
quick demo of showing you how
to utilize unique constraints.
So here we are, we're using the
recipes app we have shown you
in previous years and it is
available at download off
of the developer portal.
We've added a new feature,
import, down here in the right.
This allows me to import
all my favorite recipes
that involve apples.
Here we go back,
you see I added all
of my favorite apple recipes
however my UI isn't very
intuitive and the user may
be wondering what's going
on when they click.
Unfortunately, they have
duplicated their data.
We can do a lot better here.
Let's go back to Xcode and
look at our object model
and here we are on our
entity, I'll select a recipe
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and now we have a
new option over here
on the right called unique.
This allows me to specify
which attributes are unique
for this particular entity.
In this case we'll have
source and external ID.
Thousand when I run the
recipe app we'll see I have my
original list.
I can go, import, and I
can select my apple recipes
but I'm also impatient,
not seeing any UI
so I keep clicking.
This time we're left with
the one single object for all
of them, no duplicates, no
extra code to find or create,
your unique constraints ensure
that your uniqueness is there.
[Applause]
>> However, having all of
those duplicates isn't ideal.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
>> However, having all of
those duplicates isn't ideal.
Getting rid of the duplicates
can be a lot of work.
That's where Scott Perry
is going to come up
and show us what we
can do about that.
[Applause]
>> Thanks, Rishi.
So let's say you
already have an app
and you've got all this
duplicated data now,
now you have to go
and delete them all.
Today what you have to do is
fetch all of them from memory --
sorry, from the store -- once
they're in memory, you mark each
of them for deletion and
then you have to save
down to the persistent
store, if you have a lot
of objects you're going to have
to do that over and over again
in order to maintain a low
enough memory footprint
so that your app stays alive.
It seems kind of silly to just
load objects into memory just
so you can delete them.
This year we have introduced
a new API in the form
of NSBatch delete request.
NSBatch delete request works
like NSBatch update request
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
NSBatch delete request works
like NSBatch update request
in that it acts directly
in the persistent store
without loading any
objects into memory.
You can create one
using an instance
of NSFetchRequest specifying
an entity, one or more stores
and use predicate or sources
or limits to slice up the data
in whatever interesting
ways you want.
A batch delete request returns a
box type NSBatch deletes result
and you can configure
the request
to return a successor
failure, the default,
the count of the objects that
were deleted or the object IDs
of the objects in that box.
There are a couple of
limitations to this.
Since none of the objects
are loaded in memory,
the changes are not reflected
into the context and none
of your validation
rules are run.
Relationships will be deleted
out or nullified as appropriate,
but that's all the
guarantees that you get.
There are also no
object notifications.
We think this is going to
be really helpful for people
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We think this is going to
be really helpful for people
if they have a lot of
duplicates and I would
like to show you
how it works now.
So I have here the same
recipes app with a copy
of a database I got
from my manager.
He says one of his kids got
ahold of it and added a lot
of recipes, like thousands.
If we were to go threw
this the old way,
then we would just fetch
all of the objects we want
to delete with a fetch request.
Then iterate over all
of them, deleting them,
and then saving the
changes with the batch size
that we've configured
to be 1,000.
If you try doing
this you can see
in the console here
it takes a while.
You can see we're doing --
since we're doing
batches in the thousands,
we're now in our first
batch and it's still going.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
This will take a while.
We're not going to stand
here and wait for it.
If we break in a convenient
spot we can kill the app
and try again using
batch deletions.
Let's get rid of all this.
Creating a duplicate delete
request using the same fetch
request that we used
before and we're going
to choose a counter resultType
so that we can see
what we have done.
Here we will execute it.
It is a lot less code, there's
just one single execute request,
no looping, no interacting
with objects.
If we build and run this,
-- we'll come back here.
You can see here that in the
queries the generator has
created a trigger that deletes
all of the relationships
that need to be cleaned
up and we're done.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that need to be cleaned
up and we're done.
Back down to a simple
number of recipes
so that now we can apply
the unique restraints.
[Applause]
>> Scott Perry: That's
NSBatch delete request.
Next up I wanted to talk
about model versioning.
While we were creating the
new version of the recipes app
in order to support the
import feature we had
to add two attributes to
the recipe entity source
and external ID which
Rishi showed you earlier.
While working on this,
we open up the model,
adding the two attributes
build and run
and right away we had an error.
I highlighted the
most important part.
We incurred a migration because
the model changed but we forgot
to include the original
source model
because it was what we used to
change and the pattern of having
to copy your old model in order
to create a new one
is really cumbersome
when you're reiterating
your apps.
And if you forget to
deploy a model to the hands
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And if you forget to
deploy a model to the hands
of a customer that's running
that version it's
really dangerous.
This seems to be a case
where automatic lightweight
migrations should work for you.
Now iOS 9 and OS X.11
we have model caching.
Whenever you have a store
that's created or migrated
or just opened on the new iOS
from an older version the
managed object model used
to create it is cached into
the store and it is used
by lightweight migrations
when they fail
to find appropriate source model
as sort of a last-ditch effort.
[Applause]
>> Scott Perry: There are
a couple of limitations,
this is only available
for SQLite stores
and the cached model
is not available
for heavyweight migrations.
If you're doing heavyweight
migrations you have your model
ready anyways because you need
to know what you're actually
transitioning from and to.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to know what you're actually
transitioning from and to.
Rishi talked earlier about
API we added and I wanted
to talk now about some changes.
For iOS 9 and OS X 10.10 Core
Data has adopted all the new
language features you have seen
in Objective-C running
generics and nullability.
We have also taken advantage
of a new attribute called kind
of that allows for
easier downcasting.
You may not have seen
this in other talks
but this is really handy for
Core Data because normally
if you're interacting with
an objective type ID you can
downcast it to anything, even
completely inappropriate types.
But using kind of you
can attribute type
to only be downcast to
subtypes of that type.
This is going to add a
lot of safety to your code
from the compiler because it
will limit warnings whenever a
cache seems like it
doesn't make any sense.
Generated subclasses have also
been updated to use generics
for too many relationships
as well as nullability
and we have made
some other changes
to subclass generation as well.
In Xcode 6 you would get
an implementation file
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
In Xcode 6 you would get
an implementation file
and a header file for using
Objective-C containing both the
Core Data declarations as well
as a place to put all your code
and it was thrown over the
fence for you to own afterwards.
If you change your
model, that becomes sort
of cumbersome to
keep up to date.
In Xcode 7 we have
added a new file.
This file is an extension
or category depending
on the language that contains
all of the declarations
that you're familiar
with from the header.
So now the header and
implementation file are yours
to own and whenever you
update your model all you need
to do is update this file.
[Applause]
>> Scott Perry: That's
it for changes.
I wanted to talk about
deprecations, we're getting rid
of confinement currency
in iOs9 and OS 10.10--
it is marked as deprecated,
we're getting rid of it later.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
it is marked as deprecated,
we're getting rid of it later.
Because the confinement
was the default behavior
for new managed object context,
we have also deprecated in it,
and so moving forward
you should be using init
with con currency type
using private cues
or main cues for your contexts.
If you haven't already moved
over to the block API it
is really a good idea.
Encapsulation makes it
a lot easier to reason
about your model code and
the concurrency debugging is
reported as much stronger.
I highly recommend checking
out the online documentation,
the Core Data guides were
completely updated this year
and Adam Swift also
introduced the block API
with this really
good talk What's New
in Core Data on iOS
in WWDC 2011.
Last I wanted to talk
about performance.
Over time we start adding
attributes to the models,
the amount of data
that's carried
by your users gets larger
as they hold on to our apps
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
by your users gets larger
as they hold on to our apps
over the years and the ways in
which we try to query the data
to show them get more
interesting, more advanced,
and our apps stay fast.
But how do you avoid
being surprised
by performance problems?
When you're in development
you're dealing
with a known data set
that may be smaller
than what your customers
are working with
and the simulator is much
faster than moving on a device.
While that's great
for development,
the users are using devices
with production data.
Luckily we provide tools that
allow you to spot patterns
that are indicative of
performance problems
so you can solve them
before they become problems
in the hands of your clients.
I'd like to talk about three
things to look out for,
the first of which is
relationship faults.
This is the Core Data Instrument
and we have just ran the
recipes app and right away
in the cache missed
Instruments we can see
that we blew the cache on three
objects we wanted to display.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that we blew the cache on three
objects we wanted to display.
If we look in the middle column
we can see their recipe type.
And now you remember when we
made the recipes app this year
we updated it so that the
main list view showed the type
of the recipe along
with the recipe itself
but we never changed
fetch requests.
We can fix this by adding
a relationship key pad
for prefetching to
the query we used
to see our NSFetchResults
controller.
Those first -- that first set
of cache misses will now
no longer be a problem.
If we go back to
the same Instrument,
and look a little
bit later in the app,
we can see that when
we viewed the detail
of a recipe we also
incurred a number
of careers to the database.
This is because the detailed
view controller gets its model
object from the list and then in
the detailed view we display all
of the ingredients
in that recipe.
We can't use prefetching,
because then we would prefetch
all of the ingredients for all
of the recipes displayed
in the list view.
In the detailed view,
in the controller,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
In the detailed view,
in the controller,
we have to execute
another fetch request
to get those ingredients
up into memory.
Now we have turned
9 queries into one
and we can still use the
relationship on the recipe
to reverse it and interact
with the set it returns
because the data is
shared on the objects.
Lastly, if we look at
the fetch Instrument
in the Core Data
Instruments view we can see
that that first fetch request
took longer than we would like.
It fetched 85 objects.
At the time we only
had 85 objects.
This is going to be really
bad if we have 30,000
like I showed in the demo.
The app probably
wouldn't even launch.
It also it took 15 milliseconds
on a Mac Pro, that's going
to have a lot of dropped
frames on iOS so what we can do
to resolve this is
add a batch size
to our fetch request that's fed
into our fetch results
controllers
so that objects are
fetched from the store only
as they're needed to display.
The last one I wanted to
show is sequence blocking.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The last one I wanted to
show is sequence blocking.
If you have really complex
fetches that take a lot
of time then you can find
them by using this argument
to your program and it will
start printing out data
about your fetch
requests as they're ran.
This case, we have a query and
the amount of time it took,
which was about, what,
almost a tenth of a second
and it returned 85 rows.
That's pretty slow.
We should take a
look at doing better.
If we scroll up higher in the
console we can see it printed
out the file we're
using and we can connect
to that file using SQLite to
figure out what's going on.
If we paste in our query
after explain query plan then
SQ lit will tell us what it is
trying to do to fulfill
the query with this table.
There is a couple of things
to note here that we can use
as a metrics for how to
expect the performance to be.
The first is scan table, scan
table means that SQLite is going
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The first is scan table, scan
table means that SQLite is going
to touch every row, inspect
every row to fulfill the query
and on the recipe table
as we had earlier,
that was 30,000 rows
and we'll do that twice,
so that's not going to
be very fast and we have
to investigate making it better.
Also, we have use temp
B-tree which is a step
where SQLite builds its own
memory structure out of the data
in order to fulfill either
sorting or fast searches.
The temp B-tree is being used
because of this group by here,
if we take a look
a little closer,
it is because of the
source in external ID.
We should be able
to make this faster
by using a compound index.
In the Core Data model
editor we can add one
on the right-hand side here.
Now, if we quit SQLite,
rebuild our project,
perform a migration, and
attach to the new database
with SQLite we can then see
that we're using an index.
Using index means that
the searches is going
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Using index means that
the searches is going
to be fast using covering
index is even better,
it means that the
results of that step are
in the natural sort order
needed for the next step.
We have entirely eliminated
the temporary B tree
but we still have
the scan table.
In this case, we're
matching duplicate objects.
This was the query we
used in the demo in order
to find objects to delete.
It has to scan the entire
table, it can't be faster.
The only thing left
to do is to make sure
that we're off the main thread.
In this case we're using
a private Q context,
but if you were trying to create
some composite data do show
to a user, you might want to
use a nonsequitous fetch request
that will get off of the main
thread while it's working
and then come back when
the results are ready.
So those are three common
patterns to look out for
in your app that allow you
to solve those performance
problems before
they're problems.
That's it this year for
What's New in Core Data.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If you find any problems,
please file them.
There is a bonus
for sample apps code
that reproduces right
away, it gets fixed first.
We're also interested in
hearing things that you would
like to see in Core
Data, feature requests,
enhancement ideas, the
documentation guides
as I said were all revised this
year, so if you find any issues
with those we would love
to know about them as well.
For more information, check
out the developer portal
and our documentation and
sample code, you can get support
on the dev forums
or through DTS.
Thank you for coming.
[Applause]