navigate site menu

Start learning with our library of video tutorials taught by experts. Get started

Core Data for iOS and OS X
John Hersey

Core Data for iOS and OS X

with Simon Allardice

 


Core Data is an object-oriented persistence framework used to manipulate and store data in iOS and OS X applications, and it's a core competency for any Apple developer. This course introduces the concepts behind Core Data: what it is, what it does, and how to get started. Author Simon Allardice explores the Core Data stack of objects and steps through the hands-on process of using Core Data in both iOS and Cocoa. The course shows how to shift from a database-focused or file-focused approach into a true object persistence approach: working with saving, loading, searching, and filtering. Discover how to model your data correctly, integrate Core Data objects with iOS and Cocoa user interfaces, and take existing data and load it into your app.
Topics include:
  • Understanding Core Data and object persistence frameworks
  • Creating a Core Data project
  • Exploring data modeling
  • Creating entities, attributes, and relationships
  • Creating managed objects
  • Fetching in Core Data
  • Implementing undo and redo support
  • Creating a Core Data Cocoa app without code
  • Responding to validation issues
  • Converting store types
  • Preloading default data

show more

author
Simon Allardice
subject
Developer, Mobile Apps, Desktop Apps, Databases
software
Mac OS X , iOS
level
Intermediate
duration
4h 29m
released
Oct 17, 2012

Share this course

Ready to join? get started


Keep up with news, tips, and latest courses.

submit Course details submit clicked more info

Please wait...

Search the closed captioning text for this course by entering the keyword you’d like to search, or browse the closed captioning text by selecting the chapter name below and choosing the video title you’d like to review.



Introduction
Welcome
00:03Hi, this is Simon Allardice, and welcome to Core Data for iOS and OS X.
00:08In this course I'll begin by talking about what Core Data is and how it can help us.
00:13We'll explore the vocabulary and the ideas of Core Data, terms like managed objects,
00:17predicates, and what these mean. But we'll quickly get hands-on.
00:21Let's start by modeling our data using the tools in Xcode to describe our applications
00:26in a way that Core Data understands and enables us to easily save or persist that information.
00:32If we've saved it, we need to be able to retrieve it.
00:34We'll see how to fetch, bring our data back, and tie it into a user interface.
00:39Beyond the most trivial of applications, Core Data is what you should be using to work with
00:44data in your iOS and Mac applications.
00:47It's an essential competency for an Apple developer. Let's get started.
Collapse this transcript
What you need to know
00:00This isn't a beginner class. Core Data is hard.
00:04It is worth it, but it's hard, and it places some expectations on you.
00:07So do this course once you know your way around Xcode and Objective-C, and when you're comfortable
00:12with making a least basic iOS or basic Cocoa applications.
00:16Now, Apple themselves will tell you that Core Data is not an entry-level technology, and
00:21it assumes knowledge of regular Cocoa or iOS development skills.
00:25And we're going to need those skills, things like delegation, target/action, key/value
00:30coding, MVC, inheritance, notifications. I expect you to know these thing.
00:35Although, if you feel like you're an expert on everything, that's okay as long as you
00:39can look things up when you need to,
00:42although I don't expect you to know anything about Core Data itself.
00:45Now, you might have attempted to learn about Core Data already,
00:48but I'm going to start here as if you hadn't.
00:51I'll begin with the idea that you've heard this term Core Data, you keep hearing it,
00:55you can tell it's something important, but you don't know much about it.
00:58You just know it's something to do with saving data in your application.
01:01Now as we'll see, it's a lot more than that, but that's where I am going to begin.
01:06Finally, because we're working with data, it is useful although not essential to have
01:11experience with relational databases.
01:13That could be Oracle or SQL Server or something simpler like Access or FileMaker.
01:18Now, make no mistake, this is not the same thing. Core Data is not a database, though
01:24we will talk about exactly what it is in just a moment.
01:27But if you have that background of dealing with hundreds or thousands of pieces of interrelated
01:32data, then you've experienced some of the same issues, ordering it, filtering it, saving,
01:37relationships, and dependencies, problems that come with the territory of working with
01:42data, whether large or small amounts of it.
Collapse this transcript
Using the exercise files
00:00If you're a Premium Member of lynda.com, or if you're watching this tutorial on a DVD, then
00:05you'll have access to exercise files for this title.
00:08I've expanded them here onto my desktop, and inside this folder, each of the relevant chapters
00:13will have its own folder, and there are some projects that I'll be working with during
00:17the course to provide some simple starter code to save us some time.
00:21We'll point out any files at the relevant time just by showing you the path to the file location.
00:26There are also some finished examples, if you want to compare and contrast what you've done with what I've done.
00:32But they are all just for convenience.
00:34If you don't have access to the exercise files, you can follow along from scratch or with your own assets.
Collapse this transcript
1. Getting Started
Learning Core Data
00:00More than any other area of iOS and Cocoa, I'll talk to developers who seem to just hit a
00:05brick wall getting started with Core Data and find it difficult to get a handle on this technology.
00:10Now let me talk about why for just a moment, because it is easy to approach Core Data the
00:14wrong way and end up wasting your time.
00:17The first problem is actually with the first question most developers ask, what exactly is Core Data?
00:23Because I can answer that, but I end up using phrases that don't really help.
00:28I can quote Apple that Core Data is quote a schema-driven object graph management and persistence framework.
00:35This is technically true, but it's not helpful.
00:37It doesn't give us any sense of what we need to do and how we would deal with it.
00:42The next problem is that most developers will jump into asking questions to try and force
00:46an equivalent to something they already know, and I'll always have to answer these with
00:50a phrase that begins, "No, but..."
00:52So is Core Data a database? No, but it might use one.
00:57If they have a Java background, they might ask is Core Data like Hibernate? Again, no, but.
01:03Or if they come from .NET, they might ask is it like entity framework, or is it like some
01:08other technology you might know on a different platform?
01:11And once again, no, but there might be some comparisons sure, because a lot of things
01:17deal with the same problem space.
01:20However, this whole approach is doomed from the beginning, and I suggest you avoid it,
01:24and don't obsess about trying to make Core Data exactly like something you already know. It isn't.
01:29Now if you have ever started to read a tutorial on Core Data, you'll find that most will quickly
01:35hit you with architectural diagrams.
01:38What's often referred to as the Core Data stack, the technical pieces of this framework,
01:43all these new objects we now have access to and how they interact with each other.
01:48And you will see this kind of thing a lot in more or less detail, but this like most
01:52architectural diagrams is once again technically true but only useful to people who know most of it already.
01:59Whenever you see something like this, you're seeing the result of someone who's worked
02:02their way through a technology and is writing about it from the other side.
02:07So for them, this diagram is a good compact reminder. It's useful and contextual because
02:12they already know which of these things are the most important, which take the most work,
02:16what order they are done in a project, which classes they need to care about, and which they don't.
02:21And this diagram tells us none of that. So we are not going to do it this way.
02:25We are going to take a different approach to learning Core Data.
02:28We will see this popular Core Data stack diagram, but we'll see it later on when it helps.
02:34However, let me bring it back for a second, because this diagram does have one thing right,
02:39and there is a reason I show it to you now.
02:41Now don't worry about what all these names represent right now.
02:45Just understand that Core Data is not one piece.
02:49It's not one really important class, nor like many other frameworks in Apple development
02:54is it a bunch of vaguely-related utility classes you just pick and choose from as you see fit.
03:00Instead, Core Data is several large pieces of functionality designed to work together.
03:06It is a machine full of moving parts,
03:09objects deeply dependent on other objects that's being handed to you by Apple.
03:13And that's what can make it difficult to approach.
03:16If we can pull this apart and take it piece by independent piece, it would be much easier,
03:21but there are no single parts of Core Data that makes sense all by themselves.
03:25It's all or nothing.
03:27You try and focus just on one piece, and you'll find it drags all the others along with it,
03:32and that makes things a little different when we are learning it.
03:35Think about it this way.
03:36When you first learn regular iOS or Cocoa development, or indeed pretty much any programming
03:41language, there are ways to smooth the learning curve.
03:45You can begin by creating a Hello World application.
03:48Then you can make a simple app that works with integers.
03:50You can make one with strings. Then you learn how to work with dates.
03:54You make an app with one button.
03:56You can make an app with a button and a slider.
03:58You can slowly learn new objects one by one and grow your knowledge bit by bit, and that
04:04kind of learning curve doesn't really stop.
04:07You can go on like this for years, but learning Core Data on the other hand, that's kind of like
04:12learning to ride a bicycle.
04:14You have to do it all at once, and you have to do it all up front.
04:18You don't learn to ride a bicycle by taking it apart.
04:21You don't spend a day first just spinning the right pedals and then a day spinning the
04:25left pedals and then a day moving the handlebars.
04:27You have to do it all together, balancing, steering, pedals, brakes. At some point you
04:32will have to get on and do all of it at the same time.
04:35Because up until the point where you can do all of these things together, there is actually
04:40no benefit to just knowing one part of it. That's the same with Core Data.
04:45There's no benefit to knowing just one part of this.
04:47You need all of it working together.
04:50What that means is that Core Data is a steep learning curve, although what's tough to see
04:55from here is it's a short learning curve.
04:58You must have all the pieces in place to make Core Data work at all, but as soon as it is
05:02in place you're done. You're rolling. It's all downhill from there.
05:06We just need to get over that first hump.
05:09There are several things to learn, and we will need to get them all moving at the same time.
05:14So what are those things? How do we start?
05:16Well, the best way to begin is actually by asking a different first question.
05:22Not what, what exactly is Core Data technically, but first, why?
05:27Why does this exist, why was Core Data invented, and what problems is it a solution for?
Collapse this transcript
Why use Core Data?
00:00So here's the world we're living in. Let's say I've written an application.
00:04It could be on the iPhone, the iPad, the desktop.
00:07That really doesn't matter.
00:08I use this app, and as I do my code is instantiating objects,
00:13some of these are simple, NSString, NSSDate, NSArrays.
00:16More importantly, I have my own custom objects from classes I've written myself for this application.
00:23My model objects the M part of MVC.
00:27Now I might have objects that themselves contain other objects or have a reference from one
00:31to another, and I end up with perhaps a handful of these, but perhaps hundreds or thousands of objects.
00:36Now this is what's referred to as an object graph, meaning at a particular instant in
00:41time the state of all of the objects in my application and how they are related.
00:46But now I need to exit this application.
00:48If I'm on iOS, I'd press the Home button and switch to another app.
00:52Maybe I get a call, maybe my battery runs out.
00:54If I'm on the Mac, I could quit the app or shut down my machine, but I leave the application. So what happens?
01:00Well, all these objects just go away.
01:03Unless I've provided some code, some way of saving them they will just disappear from
01:08memory, and the next time I open the app we start again from the beginning.
01:13There are a couple of exceptions.
01:15If you're working with iOS, a value entered into a standard user interface element will
01:20be kept in the background state.
01:22So that app can be quickly reopened. But that's just convenience.
01:25That won't survive a reboot of the device, and it certainly won't save any objects behind the scenes.
01:31This behavior is not always a problem.
01:34Something like a calculator app might be just fine that way.
01:38When you finish, it forgets everything, but when you start you always start from the beginning
01:43as if it was the first time you'd run the application. We know this.
01:47We know we sometimes want to save.
01:50But it's very easy to think of saving as something you do for documents or spreadsheets,
01:55large files that you give a name to. But in fact, it's much wider than that.
02:00You expect that when you turn off your iPhone, you still have your photos, your mail, your
02:04settings, your music, the cities in your weather application, the stocks you look up in your stocks
02:09app, the high scores in your games or what level you got to, the songs in GarageBand,
02:13and of course, yes, the documents and spreadsheets you've written.
02:18So as a developer, we need the state of our objects, the information in our app, or at
02:23least some of it the most important information to be savable to not require the application to be running.
02:30This is what we mean by the term persistence, that this data can persist.
02:34It can outlive any process that is using it.
02:37Now I am talking here first about persistent locally, saving directly to the storage on
02:42an iOS device or saving to the hard drive on a desktop or laptop and being able to quickly
02:48read that information back when the application is opened again.
02:53But of course, we do already have ways we can do this.
02:56There are multiple ways I can deal with this problem without having Core Data.
03:00And it's worth talking about those for just a second, what are the alternatives?
03:05Well, alternative number one, do nothing. This is your default.
03:09Don't do any saving of your data, fine for some apps, not fine for most app once they
03:13do anything remotely interesting. So we will talk no more on this.
03:18The second option, good old flat files, plain text, XML, property list files.
03:23You can write code to save your data to these files. Probably the most useful and convenient
03:28in Apple development are property list or Plist files as several of the basic built-in
03:34objects, like NSStrings, NSDates, NSDictionary have methods to write their contents directly to Plist file.
03:41So this is very simple as long as you have very straightforward data, but you can't just
03:47write any object into a Plist.
03:49So it's not good if you have complex objects or a lot of objects.
03:53The next alternative would be archiving and unarchiving.
03:57If you have more complex custom objects, you can write your new classes to support NSCoder,
04:03which means in your class, you are providing methods for encoding and decoding your objects,
04:08which really just means writing code to break your objects apart into their individual pieces of
04:14data so that they can be archived or stored into a flat file and then unarchived from
04:19that flat file back into an object.
04:22One popular option for persistence is SQL Lite, or SQLite, a lightweight embedded relational
04:29database engine that is already available in iOS.
04:32It's built into the SDK, and it's available on other platforms too.
04:36So in your application, you'd write code to create and connect to database tables, then
04:41to save and read from those tables using SQL statements.
04:46Now one benefit is that many developers already know SQL and SQL, but there's a couple of downsides.
04:52Working with SQLite requires C calls rather than Objective-C.
04:56So that can be a little tedious, and it still really needs us to pull our objects apart
05:01to be able to put them in a relational database.
05:04As we'll see a little later we will often use SQLite with Core Data, but that is simply
05:11using it behind the scenes as a storage format and is transparent to us.
05:16We don't need to know the details of SQLite to do that.
05:20The last alternative I am going to talk about here is to use something like iCloud or
05:24web services to save your data over the network to a remote location, using formats like SOAP
05:32or JSON and fetching it back again later.
05:35But even if you're interested in using cloud- based storage, if you completely rely on this,
05:40it'll mean that if you have no network connection, you have no data.
05:44So people often want persistent data locally, even if they are fetching it from a server.
05:50Now with all of these alternatives to Core Data, you have to do some work.
05:54Some of them like property lists are very easy to get started with, but the more data
05:59you have and the more complex that data becomes, the more work you have to do.
06:03Now here is the biggest difference, though.
06:06These methods that I have just described all expect you to break your objects apart.
06:11We might use terms like serialization and deserialization, or archiving and unarchiving,
06:17encoding or decoding, but the idea is the same.
06:20You are writing code to break them apart, flatten your objects out. Stop thinking in
06:25object-oriented terms in order to able to save your objects.
06:29Well, Core Data is different.
06:32Core Data will let us continue to work with objects, to think in objects, to be object-oriented.
06:39So it is a more relevant way, a more natural way of doing persistence than any of the alternatives,
06:45and because of that, it will also provide more for us than just saving our data, as you'll see in a moment.
Collapse this transcript
Understanding what Core Data provides
00:00The aim of Core Data, the way that it's different is that it allows us to take the state of
00:05all the interconnected objects in our application, meaning our object graph and just saying save
00:12and not iterating programmatically through all of these objects one at a time calling
00:17save on each one and then trying to re-create those exactly as they were saved,
00:22but instead being able to instantly take the whole thing and just freeze dry it in place
00:27and store it to take a snapshot and store it on the device or the laptop hard drive.
00:32Well, I say save everything, but you know, typically you're not really interested in all the objects.
00:38You are only interested in the important objects.
00:41You don't usually need or even want to save every single object in a running application
00:46because you'll often have lots of temporary working variables, and things like user interface
00:50objects are already stored in your NIB files. So a subset of objects, the important stuff.
00:57Typically the M of MVC of Model View Controller, meaning your model objects created from your custom classes,
01:04the information that's unique and important in your application, whatever that is.
01:10To discard the temporary objects, persist your objects and then when the application opens
01:16again, just quickly load them back in and have everything reconstituted, all the objects,
01:21their data, and their relationships with other objects re-created exactly as they were, and
01:26this is what Core Data does.
01:29You are not writing a whole bunch of code to deal with archiving and un-archiving, flattening
01:34objects out, reconstructing them, worrying about how one object connects to another object,
01:39that's all taken care of by Core Data.
01:42So your app starts running again, and then you just create any of the additional objects
01:46it needs that time around, and this is what is meant with the phrase Core Data is an object graph persistence framework.
01:54It's not about saving unconnected independent pieces of data, it's about saving a snapshot
01:58of your objects at a point in time, whether that's one object or a thousand interconnected
02:04ones, that is what Core Data provides. But wait, there is more, that's only part of it.
02:10See, when you use the other options for persistence, here's how development usually goes.
02:15You create an app and write a couple of custom classes.
02:18You then write some code to save and load them from some conventional store property
02:23list files, SQLite, whatever that store is.
02:26Now that part, just getting started saving to property list files or SQLite, is usually pretty easy.
02:33But after that, things rapidly take a more difficult turn.
02:36As your application becomes more complex, well, first you're going to start adding some
02:41validation code to make sure the objects aren't ever re-created in some invalid state, and
02:46that's particularly important when you have objects that reference other objects and
02:50you need to guarantee all those references are still meaningful.
02:54Next, you might write some code to handle undo and redo to be able to back out changes to those objects.
03:00Make sure that undoing a change to one object doesn't invalidate another one, and if you
03:05have a lot of data, you don't just want to load it all up front because that's going to
03:09delay the start-up time and be bad for performance.
03:12So you'll start writing code to try and load data more efficiently only when needed, what's
03:16sometimes called Lazy Loading, and after that we might need some code to be able to search
03:21all this data that you are storing.
03:23With most persistent options, most choices like property lists, flat files, SQLite, here's
03:29how these responsibilities break down.
03:32This is what your storage mechanism takes care of, which is pretty much nothing except storing the data.
03:39And all this, everything else is you, it's your responsibility to write, it's what you
03:44need to do in your application. That will be the case for property lists, XML files
03:48using classes with NSCoder method using SQLite.
03:52But when we use Core Data, we turn that on its head because all of this is in Core Data,
03:59and then this is you.
04:01When you opt into using Core Data, you get a huge amount of functionality provided for free.
04:07Core Data doesn't just help you store your information, it can validate and keep validating
04:11your objects over their lifetime.
04:13It helps you manage changes to your objects with undo and redo support built right in.
04:17We have lazy loading only loading what's necessary rather than reconstituting everything
04:22into active memory at once.
04:23It can help you search through all the data you have and only return what you need.
04:27And it does more, but that's enough to get started with.
04:30And this is why when people ask things like is Core Data a database?
04:34The answer is no, it's more than that.
04:36Sure, it might take care of storing the data like a database would, but it also adds all
04:41the rest of this functionality in a way that other persistence options don't, and it does
04:45this all in a way that's inherently object-oriented and easy to connect to iOS or Cocoa user interfaces,
04:52and it's already written.
04:53We don't need to build Core Data, we need to join in.
04:58We need to hook into this Core Data machine that's already running, and that is our challenge,
05:04hooking our application into Core Data, and here's how we're going to do it.
05:09To learn how to hook into Core Data in this course, we will do it in four phases.
05:14The first phase is describing or modeling.
05:17The most important thing to get started is not writing code or creating user interfaces, it's modeling.
05:24Creating a data model of our most important objects in our application.
05:28It's us describing to Core Data what's in our app that's important, what objects we
05:34want it to take care of.
05:36Data modeling is one of those terms that can be used in an abstract fashion.
05:41Something you think about doing on a whiteboard or on paper.
05:43Now that is not what it means here. We will have an actual data model file in our Xcode
05:49project that's as important and specific as a ViewController Class file or a NIB file.
05:55It is truly part of our application, and any change to the model is a change to the app.
06:01Now once we've done that, we'll move on to telling Core Data how to save,
06:06how at the time of our choosing we can take the modeled objects in our application, our
06:10object graph and persist it, save it. We'll see the classes and the methods that we need
06:14to use to do this and the options that we have. We'll then move on to phase three of fetching,
06:20how to fetch or retrieve the data that we've saved, how we can quickly filter it,
06:24sort it, ask questions of it.
06:26Now, if you're coming from a conventional database background, know that this is not done using SQL.
06:31It's done using something called Predicates, and we'll see how to make and use these,
06:35and when needed, ways of putting the resulting data into our user interface elements.
06:40And phase four, everything else.
06:42This covers a lot, but once we know how to model to save and to fetch from Core Data,
06:48we have a great foundation for everything else, undo and redo, performance options like
06:53lazy loading, versioning our applications importing data, and much more, and more complex
06:59modeling, saving, and fetching.
07:00And we'll also revisit that Core Data stack diagram when it will make a lot more sense.
07:05I have been very concept-heavy these last couple of movies to describe what I think
07:11is the best way to approach Core Data and avoid some of the common misunderstandings I've seen.
07:16But we are going to get hands-on right now.
07:19I'm going to jump into Xcode next and do a quick demo using Core Data where we will see
07:24the first of these three phases: modeling, saving, and fetching, and we'll start to get
07:29familiar with the vocabulary and the terms we're going to use a lot, now that we're living
07:32in the world of Core Data.
Collapse this transcript
Creating a Core Data project
00:00The easiest way to get started with Core Data is to do almost nothing.
00:04Now you might think, great, I've been doing that already.
00:06But let me show you what I mean.
00:08Jumping into Xcode, I'm going to create a new project.
00:11Now Core Data is a framework that we can link to and use in any iOS or Cocoa application.
00:17And if you have an existing project which you've written so far without Core Data, then
00:21yes, you absolutely can add Core Data support to that project.
00:26But when you're first learning Core Data, learn it by creating some new projects, even
00:30if you end up just deleting them.
00:31This is a much easier way to get all the pieces organized in your head before you try and
00:35retrofit those pieces into an existing project with a lot of code.
00:40So in Xcode, three of the built-in project templates for iOS have an option, a check
00:45box to include Core Data support.
00:49And so does the regular Cocoa Application for desktop development.
00:54And the benefit of checking this is not that our project will link to Core Data.
00:58We could do that anytime.
00:59There's nothing magical about linking to a framework,
01:02but that it will also add some code and a new file to this project to get us up and
01:06running with Core Data. It will simply save us some time.
01:09And I can demonstrate some code here, without you having to watch me type it all in.
01:13So I'm going to do this first demonstration using an iOS application because I suspect
01:17that's slightly more of my audience here, but we will see some Cocoa-specific examples
01:22later, and the basic techniques are the same for both.
01:26So in Xcode, the three templates for iOS that have a Core Data option are the Master-Detail
01:31Application, the Utility Application, and the Empty Application.
01:35And usually it's not an option when you're creating a good old single-view application,
01:40or at least not at the time that I'm recording this course.
01:43There is no Core Data check box here. Now, this does not mean that single-view apps are
01:48prohibited from using Core Data. They don't have a problem using Core Data at all.
01:52But you just have to add it yourself manually, linked to the framework and manually add the necessary code.
01:57And by the time you're done with this course, you'll know how.
02:00But to begin with, I'm going to take the easy option.
02:02I'm going to create an iOS Master-Detail Application, click Next, I'll call this CoreDataDemo.
02:11I'll select it for the iPhone. I'll use a couple of the modern development techniques.
02:15We are going to have Storyboards checked and Automatic Reference Counting checked.
02:20Everything I do in this course is going to be with arc turned on, and of course, the important one
02:25Use Core Data, not going to bother with Unit Tests so that will be unchecked, and going
02:30ahead, I'll save this to my Desktop, and I'm going to make sure that the local git repository
02:35is unchecked as well.
02:38And then I'll just go ahead and run this application.
02:41This does not actually require anything else. It is a working albeit simple Core Data application
02:47right out of the box. Everything is already in place.
02:51So the application opens, and if I press the plus button, what I'm doing here is instantiating
02:56a new although very simple object, this object just has a timestamp property, and I'm showing
03:02these objects in this table view here.
03:05I can select any of them to drop into the detail view which shows me the details for
03:09just a single object.
03:11Again, it's just a timestamp and then back up to the table view, and I can swipe any
03:16one of these and delete that object.
03:18It doesn't look remarkable, but every time I'm creating one of these objects it is being
03:23saved, persisted by Core Data.
03:26So I would expect that if I leave this application and come back in that I would see those objects again.
03:32But let me demonstrate something else.
03:34Now you don't need to duplicate this, but I'm going to show it because you might find it useful.
03:38On the simulator, I also have an app that I created a little earlier that is the regular
03:44iOS Master-Detail app but without Core Data. I've just called it WithoutCD.
03:49Now if I open that up, it looks pretty much identical. I can use the plus button and add, say, three objects.
03:55I can drop in and see the detail view. I can go back up and swipe to delete.
04:01And if I go out of this WithoutCD and back in, I see those objects. It looks like it's saving data.
04:08But really here, this is just the behavior of iOS going to the background and foreground
04:13modes of this application.
04:15If I actually truly terminate this app--and one easy way I could do here is just double-click
04:20the Home button, hold down and click the minus button here and then, open it up again--
04:26we have nothing. There is no real persistence going on here. Nothing is being saved.
04:31But on the other hand ,if I do that with the Core Data app, and first what I'm going to
04:36do is just stop it running in Xcode because I don't want Xcode to get a little confused
04:40if I terminate the application. I open it up, we see these five objects.
04:45I close it down, I'm now going to double-click and both terminate the Core Data version and
04:51the non-Core Data version.
04:53Without Core Data we have nothing, with Core Data we have persistence.
04:58These objects are being saved to the file system of the iOS device, in this case the simulator.
05:04These objects are not dependent on the lifetime of the app that uses them.
05:08So going back into Core Data, what is the difference, then, with a Master-Detail project
05:13with the Core Data check box and Master-Detail project without that Core Data check box?
05:18Well, if I were to look through a couple of them and compare,
05:21I would see that the storyboard is exactly the same. It's a Navigation Controller that
05:26contains a Master View Controller with a table view that just goes down to a Detail View Controller.
05:32All the code for the Detail View Controller, if you compared and contrasted, would be exactly the same.
05:38What's different is three things. First, we're linking to the Core Data framework.
05:43Second, some code has been added to the app delegate and to the Master Detail View Controller.
05:50To make sure that the table view is being fueled by Core Data rather than just being
05:55fueled by a mutable array, which is the way it would be in a regular iOS master detail project
06:01template without Core Data support.
06:03Now this code uses several of the Core Data specific objects and is likely to look a little confusing.
06:08But that's okay, this is not where we need to look first,
06:11because what we're interested in is the final addition to this project.
06:15But there's one new file in the project which is this one, CoreDataDemo.xcdatamodeld, the
06:23Xcode data model file. This is our starting point with Core Data.
06:28Before we get into writing code, before we start seeing how to save, how to fetch, undo/redo,
06:32we need the data model.
06:36This file represents the contract between our application and Core Data, where we tell
06:41Core Data what it's expected to manage.
06:43Now in this case, it is a really simple data model.
06:46What its doing is describing something called an event entity which has a timestamp, and
06:52this is what's being created instantiated as an object in our application.
06:57But as we'll see, this data model could be a lot more complex, and it's this, the data
07:01model idea, this editor in Xcode, these terms you see like entities and attributes, relationships.
07:08This is what we're going to look at in the next section our first phase of working with
07:12Core Data, which is modeling.
Collapse this transcript
2. Modeling in Core Data
Introduction to data modeling in Core Data
00:00So we've seen that the idea with Core Data is that we'll be able to take a group of objects
00:04and just say save, that could be one object, two, or a hundred interrelated ones.
00:10But I did mention that we don't need to save every single instantiated object, so that would
00:13be a waste of time and resources that there are many objects in a running application
00:18we'll never need to persist, temporary variables,
00:22user interface objects that are already defined in our storyboard, or NIB files, so we can ignore those.
00:28Really, it's about our own custom classes that we've written that will become our model objects, our data.
00:34Whatever that data is, simple text objects, complex binary objects,
00:38but the objects we want Core Data to save.
00:41Or you know, the best word here is not save or even persist, but manage.
00:47These are the objects we want Core Data to manage, to manage their saving and loading,
00:51to manage their undo and redo, to manage their lifetime.
00:55So these are what we will call our managed objects.
00:58There are still Objective-C objects with methods and properties.
01:03We can use them as we'd use any object, but we'll be managing their lifetime with Core Data.
01:08And when you see this term managed anywhere in iOS or Cocoa development, you might as well
01:13silently insert the words Core Data in front of it.
01:17There are classes like NSManagedObject, NSManagedObjectModel, NSManagedObjectContext, and if you see that term,
01:24assume that's what's doing the managing is Core Data. These are all Core Data classes.
01:31So when you create a project in Xcode that has Core Data support, and it includes that xcdatamodel file,
01:39this is the way that we tell Core Data which objects in our application we want it to manage.
01:44So this is not meant to be a model or a description of everything in our app, just the managed objects.
01:49And in fact, another name for this file rather than just data model is managed object model,
01:56and if we want something to be managed by Core Data, it needs to be part of this data model.
02:00The flip side of this is if it's not in this model, it is not managed by Core Data.
02:06So one question I'll get as well, if you've got some managed objects and other ones that
02:11we might think of as unmanaged objects, how do you tell Core Data which are the regular
02:15unmanaged objects that it's not supposed to touch? Well, you don't.
02:19You don't do anything with them.
02:21So for an example, if you take an existing project and just link to the Core Data framework,
02:25it will not suddenly attempt to start persisting all your regular NSStrings and NSArrays and custom objects.
02:31Core Data won't touch anything unless it's described in the model.
02:37Now when we use the word modeling, particularly things like data modeling or object modeling,
02:41it's often used to suggest a graphical approach, although that isn't necessary.
02:45Although yes, this same editor part of a project in Xcode can switch between a graphical view
02:52and a table view of the same information that we'll see a little later, but know that this is not for show.
02:59Sometimes with other programming environments, if you have a modeling tool it's kind of an add-on, an optional extra.
03:05You might use it to chart out some class diagrams and at some point perhaps even have it generate
03:10some skeleton code, but it won't actually affect your project if you don't want it to.
03:15Well, not in Xcode. This data model file is part of your application.
03:20Any change to this model is a change to the app.
03:23The same way that a storyboard file or XIB file will be turned into instantiated objects
03:29when your application builds and runs, so will this model.
03:32I said a little earlier that one name for this is actually your managed object model.
03:38Well, when you build and run this file, it will be turned into an object called an NSManagedObjectModel.
03:44Whatever you do in this data model file will be accessible in your code.
03:50Now some people wonder, could you then create this data model programmatically instead of
03:55using this file and this editor part of Xcode?
03:58Sure. The same way that you could create, say, an entire user interface purely in code, you
04:04could create this data model in code as well.
04:06But that would be amazingly tedious to do and really doesn't have any benefit.
04:11So we're going to be using this data model editor for everything we do in this course.
04:15And we're going to start off with something called Entities.
Collapse this transcript
Creating entities
00:01So there will be a data model file in any project if you select Core Data as an option,
00:06but it can also be added manually to a project.
00:09I'm just going to create a new master detail iOS demo with Core Data, same as I did a moment ago.
00:15So, finding that data model file and selecting it will open the Model Editor part of Xcode.
00:21There is quite a lot of options here, but the first, the most important building block
00:25of any data model is something called an Entity.
00:28And an Entity is how you describe the structure of your managed objects.
00:33The same way that you'd write a class in normal Objective-C that defines the structure of
00:38regular objects you can then instantiate, and we do continue to do that for regular objects.
00:43Well, in Core Data, you'll define an entity that describes the basic structure of managed objects.
00:49So there could end up being 1,000 managed objects based on any one entity, and we'll
00:54have at least one entity, you may have many.
00:57In this example, I have one entity showing up on the left-hand section of the Data Model
01:02Editor, this is called Event.
01:04I don't have to keep this. It's just the example from the template.
01:08So in the left-hand section, I can select it, and I'll see the details of that entity
01:12in this middle section here.
01:14And I can see that this entry seems to have one thing, an Attribute called timeStamp and
01:20timeStamp has a Type of Date.
01:22So this application could have multiple managed objects, all instantiated from this Event
01:28entity where each managed object will have its own timestamp.
01:32Now down here at the bottom of this section, I can add a new Entity to this, clicking the
01:37button that allows me to go up here and name it.
01:40I can also use the Editor menu in Xcode when I'm in the Model Editor.
01:45So I can add entity, and I start to see them add up over here in the left-hand section.
01:51I've also got this Fetch Requests and Configurations areas, we'll get into these later.
01:56We don't need them now, but we will always need Entities.
01:59And you'll typically name new entities like classes an uppercase first letter, singular
02:05noun, so Event, Book, Author, Employee, Account, Player, Person.
02:11And if you're thinking, well, how am I supposed to know what my entity should be in my app?
02:15Well, the question is, what would your classes be?
02:17If you weren't using Core Data, these entities would be your normal classes for your model objects.
02:23So if you are building a bookstore application, you might have classes for book and publisher
02:27and author, you'd have entities for those.
02:30If you're making a music application, you'd have entities for album and artist and track.
02:36And if you're wondering, why do I have to use this new concept of Entity?
02:39Why can't I just deal with regular Objective-C classes and objects? Well, realize that we're doing something different here.
02:47An entity might be at the same conceptual level as a class, but unlike a class, we are
02:52not trying to define behavior here.
02:54So what we're doing right now is not like, say, a UML class diagram. We are not defining
03:00method names, any behavior. An entity is pure data, no behavior, no logic.
03:06But you might say, what if I want these managed objects, these things that Core Data is going
03:12to save, what if I want them to have custom behaviors?
03:14Well, we will get to that. Yes, absolutely, they can have custom behavior, but that is
03:20not defined in the Entity. The entity is data.
03:24You're simply describing what Core Data will store, what it will persist, what it will manage.
03:29Now if you're coming from a relational database background, you might see that what we seem
03:34to be doing here looks a little similar to defining a schema for a database where our
03:38entities are like tables and the attributes look like columns in those tables, describing
03:43what kind of data those columns can hold. And that is true, there are similarities, but
03:48it only goes so far.
03:50So don't assume all those skills directly apply here, because if you start worrying
03:54about things like primary keys and foreign keys, you're going down the wrong path.
03:58It's simpler than that here.
04:00However, entities certainly can be more complex than just the timestamp.
04:04So next, we'll take a look at our options for attributes.
Collapse this transcript
Creating and configuring attributes
00:00So, let's see how to work with attributes.
00:06I'm going to just go ahead and create a new master detail project, again, using Core Data.
00:11As we've seen, selecting any entity in our data model gives us these three sections on the right.
00:17These are representing the three kinds of property that every entity consists of: Attributes,
00:22Relationships, and Fetched Properties.
00:25And this is pretty much the order of their importance, and the time that you'll spend with them--
00:28by far the most important is the Attributes where we define the data of each entity.
00:33There are relationships between entities, but there don't have to be any in bringing up
00:37the rear fetched properties.
00:39These are a little more obscure, and we'll come back to these when we talk about fetching.
00:43So what we're going to work on here are attributes.
00:45Now there is such thing as an attribute that's floating out there by itself.
00:50All attributes belong to an entity.
00:53Right now, we have one called timeStamp with a type of Date.
00:56Every attribute must have a name and a type.
00:59And just as by convention, the entity is named like a class, the attributes are named like
01:04class properties or instance variables, typically camel case like the timeStamp here.
01:09And again, that's because they're comparable.
01:12Classes and entities exist at the same level, although entities are just about data, but
01:17we expect to have objects based on both,
01:20regular objects based on regular classes, managed objects based on entities.
01:25Now if I click the Plus button here to add a new attribute--and I can also do that from
01:30that Editor menu of Adding Attribute-- we'll see it entered here.
01:35An attribute will default to a name of attribute and a Type of Undefined.
01:39Now it's not acceptable to leave it as Undefined, and you actually can't.
01:43Xcode will give us a compile error instantly if we try and leave it as undefined.
01:48So let's say I wanted to call this attribute title, and then in the Type we have options
01:54of String and Boolean and Float, Decimal, Double, several integers.
01:58And most of these data types should be no surprise at all, several numeric options,
02:03Bool, strings, date, and these are all just the classes you would expect.
02:07Strings will be NSStrings, dates will be NSDates, and all the numeric options are actually wrapped up in an NSNumber.
02:15We'll see how to use them shortly, but these should take care of most problems.
02:18Now towards the bottom are a couple of options that are a bit more complex.
02:23There's a Binary Data option, and that would allow you to store images or audio, any kind
02:29of binary data in your managed objects.
02:32And because Core Data can't list everything, there's also a Transformable type for your attributes.
02:38Now Transformable would allow you to use a custom type here as an attribute, some kind
02:43of custom structure that it can't envision right now,
02:46although what you'd have to do is you would have to provide something called a Value Transformer
02:50to explain to Core Data what this attribute is made of and how it's supposed to get data in and out of it.
02:57So we'll be focusing with the basic types here as they're going to work for us in most cases.
03:02So let me go ahead and just add a couple of new entities and some attributes to them.
03:06I'll Add a new entity, I'll call it Book.
03:09Selecting that on the left-hand side--and sometimes it's worthwhile switching off and back on
03:13to make sure I'm getting the attributes.
03:15Right now, I wouldn't expect to see any. I'm just going to add a couple here.
03:18We'll say title, which is type of String. I'll add an attribute for author and publicationDate.
03:30One thing to be aware of is it will try and reorder them alphabetically.
03:33PublicationDate should be a Date, and author and title should be Strings.
03:39Another example, adding another Entity, I'll call this one Employee.
03:49Now all I'm doing here are a few examples just to show you the kind of idea what you
03:54have to pay attention to.
03:55I'm not actually going to go ahead and use these entities right now.
04:00So I'm saying here department, firstName, and lastName are Strings, isFullTime is a Boolean,
04:05and hireDate is Date.
04:07I'm demonstrating it to show that what drives the attributes of an entity in a Core Data
04:12data model are exactly the same choices you would make for creating the properties of
04:16a typical Objective-C class.
04:18If I was creating this as a regular class, I'd have properties for department, firstName,
04:23lastName, they would be NSStrings.
04:25I'd have a Boolean property for isFullTime and have a NSDate property for hireDate,
04:30because these attributes we're defining here will become the properties of an instantiated
04:35managed object in our application, and we'll just access them like using the properties of any object.
04:41However, one effect of that is if say I try and create an attribute called description,
04:46I'm going to have a problem because description already exists as a property for every object in Objective-C.
04:53It's part of NSObject, so that wouldn't be allowed. I'd have to call that something else
04:57like summary or outline.
04:59But I actually don't need it there. I'm just going to get rid of it.
05:03Now when you're editing in the data model, pay close attention to what you have selected,
05:08because the Utilities Panel on the right, which is of course context-sensitive as usual, means
05:13that it changes not just based on whether you have an entity selected in the data model
05:18editor but also a particular attribute of the entity.
05:22Now we can't see much of a change right now, because what I'm really interested in--and
05:27when we are editing in the data model this is the one we almost always want--is the last
05:32inspector, the Data Model Inspector.
05:35And you'll see that change as I change just not from attribute to attribute but also entity to entity.
05:43And with an attribute selected, well, we have a bit more to play with.
05:47We have another right here of changing the Attribute Type, same options but just a different
05:51way of getting to them. But we also have some options below that.
05:55So, say if I have a numeric attribute of some kind, like Decimal, we can have Minimum and Maximum values.
06:02We can specify a Default value for our attributes.
06:06And that understands the type we're working with, so say in my Employee entity I had an
06:11isFullTime of Boolean, I can get the Default value here of YES or NO.
06:17What that means is that in our app when a managed object is instantiated from this entity
06:22description it will have those default values.
06:25You can also make any of these attributes Optional so that it doesn't have to be a value.
06:30If I jump over to a String attribute, I'll also see default values here.
06:34We can have some validation for the Minimum and Maximum allowable length of the string
06:39and even enter a Regular Expression that a string value would have to match.
06:43And these are just the beginnings of letting Core Data manage the validity of our objects.
06:48What it means is that in our code we want to tell Core Data to save a bunch of objects,
06:53it will inform us whether we're trying to save any invalid data.
06:56There is also an option for an attribute to be Transient. It's off by default, and you'll usually leave it off.
07:03But what it means is that any value in this attribute will not be saved when Core Data
07:09saves the manage object.
07:10So in the occasional situation where it's useful to have an attribute that you can manipulate
07:15as long as the app is running but you don't actually need to save it, perhaps there's a
07:19value that's completely dependent on the time and date that you're running the app.
07:23Now, we'll come back to Transient and to a few of the other options later in the course,
07:28simply because we won't be able to see their impact for a while.
07:31Working with these basic attributes and regular data types gives us enough to continue.
07:35Now one question I do get, particularly from developers who are looking at this like a
07:39Database Schema Editor is, well, okay, after I've defined a few basic structures for some
07:45entities, where do I go to type in a bunch of values for this? Well, you don't.
07:50This is a way that we're modeling the structure of our data.
07:54It is not a way to enter any of that data.
07:57Creating these objects, managed or otherwise is done in our application.
Collapse this transcript
Modeling relationships
00:01When you have more than one entity, you can add relationships between them as long as
00:05that makes sense, of course. Not all entities need to be related to all other entities.
00:09And by describing those relationships, when our application runs, our managed objects can
00:14have references to the other managed objects they are related to, and those references are
00:19taken care of by Core Data.
00:21So I have a couple of entities I've just created in this simple sample project here, Book and Publisher.
00:28And I have made these very straightforward as the point of this is simply to demo this model.
00:32I am not building a full application out of them.
00:36Publisher has name, address, city, state. Book has author, title, publication date.
00:42I want to describe the relationship between these.
00:46There is currently no relationship.
00:48I can look at them either in the Table Style or Graph Style, there is no link between the two.
00:52But to describe a relationship, first I describe it in plain language.
00:56For my application, what does this mean?
00:59Well, let's say in my app, every book has a publisher, and a publisher may publish many
01:05books, that's the rule.
01:06Now bear in mind what I have just described is two relationships.
01:10There is a relationship from Book to Publisher, and there is a relationship from Publisher
01:15to Book, and that's important.
01:18In Core Data it is typical to create two relationships between your two entities, one from A to B,
01:26and a separate one from B to A.
01:29And here's where I'll make another suggestion to those of you from a relational database background.
01:33There are some similarities, but creating relationships in Core Data is quite different
01:38from relationships in a conventional RDBMS.
01:41If I was thinking like a conventional database designer here, I'd probably be assuming then
01:46I need to add a Publisher attribute to this Book entity so I can treat that as a foreign key.
01:52And no, no, no, a thousand times no, don't go there, it is simpler here.
01:58We want to be able to say, you know, a book has a publisher and a publisher has books.
02:03I want to describe it at that level and let Core Data figure out the technicalities.
02:08Now in Core Data, relationships aren't objects by themselves. You don't create a detached
02:13relationship out in the ether and later link it between two entities. Instead, every relationship
02:20belongs to one entity, it is part of that entity.
02:23If I'm looking at this in Table view, I see a Relationship section for each entity the
02:28same way you see an Attributes section. So relationships belong to an entity.
02:35And that means you always need to be conscious which entity you're starting from.
02:39Now you can create relationships in either the Graph View or the Table View.
02:44I'm going to start in Table View because it gives me what I think is the right focus,
02:48I am looking at one entity at a time, first Book, then Publisher.
02:53So first, I will create the relationship from Book to Publisher and then the one from Publisher to Book.
02:58So with Book selected, I come down to the Relationship section and click the plus button.
03:03What I'm trying to do is point from Book to Publisher, so I am going to select from this
03:07dropdown destination that I am pointing to Publisher.
03:12That's it, the relationship is described, that's all we have to do.
03:17But okay, there is a little more we can do, because relationships always have a name, and
03:23the name is useful. We could have relationship, relationship 2, relationship 3, but you don't want that.
03:29The name of the relationship should pretty much always refer to the destination.
03:33Again, we are pointing from Book to the destination Publisher.
03:37So in this case, I will call this relationship publisher.
03:42Now the reason that this name is useful is that this relationship will become a property
03:48of any object created from this entity, just as all the attributes will become properties.
03:54So when I create an object from this Book entity, it will have a property called title
03:59and a property called author and a property called publication date, NSStrings, and NSDates,
04:04but we will also now have a property called publisher which will be a reference to a Publisher object.
04:11That probably sounds a little vague right now, simply because we haven't created any
04:15objects based on these entities yet.
04:17So just assume, yes, the relationship name is useful, and it's typically named for the destination.
04:23Now there is also an option here called Inverse, that's going to be important, but we can't
04:28do anything with that yet, so we'll see that one in a second.
04:32And this describes one side, Book's relationship to Publisher.
04:36So now I will do the other side of this relationship, Publisher to Book.
04:40So selecting the Publisher entity, I click the plus button for Relationships, I say this
04:45is going to be named for where we are pointing to, so I will call it book, the Destination
04:49is to Book, and that's the relationship defined.
04:54However, this relationship is a little different. See, our first relationship was what's called
05:00a two-one relationship, or one-to-one. A Book has a Publisher.
05:05Let's say those are the rules for this application.
05:08Any book has a single publisher, and that's the default kind of relationship in Core Data, just a one-to-one.
05:15But this relationship is different because a publisher could have many books, doesn't have to, but could.
05:22So making sure I have got my Utilities panel and still making sure I have my Relationship
05:26selected, because again, it is sensitive to that,
05:29I can come over here into the Data Model Inspector, and there is an option here to say this is
05:34a To-Many Relationship. I'm pointing to books. I could have many books.
05:40Now there are a few other options here for customizing this relationship.
05:45We have got things like an Optional, Minimum, and Maximum Count of the number of books that
05:50might be returned for this.
05:51We are going to come back to these later in the course. We don't need them yet.
05:56Now because this relationship does not describe just a single book that a publisher could
06:01have, but really plural, that books--I am actually going to change the name of this
06:05relationship and just name it in plural. I don't have to do that, but that would be a
06:10fairly common thing to do.
06:12Meaning that when an object is created based on this entity, it can have a property called
06:17books that will be a collection of book objects.
06:20Again, we haven't got that far. We will see that kind of thing later on.
06:24Now, switching to Graph view lets me see the diagram of these two relationships.
06:29They may be overlapping a little bit, so you can move them around, and it will do its best
06:33to make this legible and readable.
06:36And what we are seeing here with these relationships is a different kind of arrow.
06:40With the single head here that's describing a one-to-one relationship, this is Book pointing
06:45to Publisher, but we have a different style for a Publisher to Book, this is the double arrowhead,
06:51suggesting it's a To-Many Relationship.
06:55These can be selected in the Graph Editor and edited in the Inspector. Sometimes it's
07:00a little difficult to make sure you're selecting the right thing.
07:03So these relationships are defined, but there is one last thing I need to do.
07:07I have created these as two relationships which is the way we have to do.
07:11But they really are just different perspectives of the same idea that publishers have books.
07:16So what I'm going to do is explicitly tell Core Data that the relationship from Book
07:20to Publisher is the inverse of the relationship from Publisher to Book.
07:25It will still keep my rules of the one is a To-Many and one is a To-One, that's fine.
07:30I just want to say yes, they are connected.
07:33And I can do this by selecting either of the relationships, and if I'm in the Graph View,
07:39I can use the Data Model Inspector and select the Inverse Relationship here.
07:43I can also do it in the Table Editor, either way around, it doesn't actually matter here.
07:48So selecting Publisher, I find the relationship of books, and I say that the inverse is the
07:53one from Book pointing to Publisher.
07:56If I jump to my Book entity, I will see that it's automatically selected the other inverse there.
08:01If I jump into the Graph view, I see that they've been combined into one.
08:06They are still considered two relationships, and I could actually pull them apart by just
08:11turning off that inverse, but they both now realize they are the inverse of each other.
08:16Now while this is not technically essential, it is highly recommended, and we do this to
08:21affirm to Core Data that these are two approaches to the same relationship and that a change
08:26one way will have an impact the other way.
08:29And what that lets us do is keep the object graph more controlled and consistent, because
08:35most relationships are bi-directional like this, so it's very, very common to do this.
08:39And actually, most of the time you'll get a compile warning if you don't define the
08:44inverse when it seems like there is one.
08:46But those Inverse Relationships can't be selected until you've created both relationships both ways.
08:53So these are the basics.
08:55We are going to get into more of the options of these relationships later on.
08:59Now of course, we're still at the point where we can't see the full benefits of doing all
09:03this work, as we don't have the other pieces in place yet.
09:06But defining relationships will let us retrieve this information much more flexibly later
09:10on and have a more complex object graph of objects connected to other objects but with
09:15Core Data maintaining the references between them, rather than us having to keep track of that.
Collapse this transcript
3. Saving in Core Data
Creating managed objects
00:00So we can take modeling further than that, but let's not.
00:04Right now, let's take what we know, make a simple managed object model, and see how to use it.
00:09Because the entire point of defining these entities is that we are going to instantiate
00:14managed objects based on those entities, we're going to use those objects, and then we are
00:19going to get Core Data to save them.
00:22And that's what we are going to do in this next section: create, use, and save managed objects.
00:27And by the time we're done here, we'll naturally run into a few more pieces of the Core Data puzzle,
00:31things like context and store coordinators, and we'll also answer the question if we are
00:37saving these managed objects, where do they actually go, where do they save to?
00:42So let's get to it.
00:43In Xcode, I am going to create a new Cocoa Application this time, so under OS X select Cocoa Application.
00:50If you have never done Cocoa Apps, don't worry, you will be able to follow along just fine.
00:55I am going to call it SaveDemo, and the only thing I am going to pay attention to here
01:00is just looking at the Company Identifier for a moment. I have got com.company, that's fine.
01:05It's not important what it is. I just want to remember it, it will come in handy later.
01:09I am now going to have an App Store Category, Document-Based Application is unchecked, I
01:14want Core Data checked, and Use Automatic Reference Counting checked, and that should do it.
01:20Click Next, and I'll just save this to my Desktop.
01:23Now when I make a Cocoa Application with the Core Data check box selected, I don't get
01:30a sample application the way I did with a Master-Detail iOS template.
01:34If I go ahead and run this, there is nothing here.
01:38We need to add everything that's necessary here, and I'm going to make just a simple one-button application.
01:44But if I go back into Xcode, I see that I do have a data model file here in this project,
01:49although it's completely empty, there is no entities in it, and I do have some of the
01:53Core Data code has actually been added into my app delegate. We'll see that a little later on.
01:58So I am going to jump into the model here and just add a new entity. I'll call this
02:04Course, and I'll just give it three attributes to be simple here, title, author, and I'll
02:15call it release date.
02:17They are all undefined right now. That's not acceptable, I want the title and author to be strings.
02:23One thing I can do here is using Command, I can click on title, then Command-click on
02:27author and with both of these selected in the Inspector change their attributes to string.
02:33That can come in handy when you've got four or five at a time you want to do.
02:37Release date, I'll just change manually to date, pretty much as simple an entity as I can imagine here.
02:43Now what I am going to do is jump over to the interface.
02:45So I am going to click on the MainMenu.xib file, and I want the main Window part of
02:51this so I can select that in the dock, either in the minimized or the expanded view, I just
02:56want a Window so that I see it here.
02:59From my Object Library and my Inspectors, I am going to drag on a Push button and just
03:04give this a name, shrink down the window because it is pretty simple.
03:12This button is going to instantiate a managed object based on that entity I've just written,
03:16and then change the properties of that object and make sure it's saved.
03:20I am just going to give myself a bit more screen real estate here and switch into Assistant
03:25View, and I should be bringing up my AppDelegate on the other side here.
03:31If you are coming from iOS, know that in Cocoa I don't have to have a formal separate view
03:36controller class file. I can just have all of my code in the AppDelegate for a simple app like this.
03:41So I am going to make this button just call an IBAction here, so I'll Ctrl-click from
03:46the button over down here in the AppDelegate and let go.
03:50I want to make sure I'm creating an Action, not an Outlet, and I'll call it createObject and hit Connect.
03:58That's everything I need to do in the header for now. I am just going to give myself my
04:01regular editor back and jump into the implementation file for AppDelegate.
04:08I'll use the jump bar to find that new method I created, createObject.
04:11Here is where I am going to write all the code that I need.
04:15Now normally when we instantiate an object, we are going to use alloc init or some variant
04:20of that, perhaps a helper method of a class, where we'd have a couple of pieces of information,
04:25the name and the type of the object pointer we are creating and the name of the class
04:29we are creating it from.
04:31But when I am instantiating a managed object, which is what I want to do right now, we need
04:36a little different information.
04:37Well, first, I do just want to create a managed object pointer.
04:41So I am going to start typing NSManagedObject. There we go.
04:46And I'll give it a name like myMO for my managed object.
04:51But I don't just want to say alloc init, because I need to say this managed object is based
04:57on a particular entity that we've described in our model, the Course entity that I just wrote.
05:03And to do this, Core Data requires not just the entity name but another piece of information.
05:09So here's the classic most convenient way to create a managed object from a named entity.
05:15I am actually going to use the NSEntityDescription class, which has a method called insertNewObjectForEntityForName:
05:25inManagedObjectContext, so let me just enter that one in and split it across a couple
05:31of different lines so I can talk about it a bit easier here.
05:35There are other ways to create a managed object. This is the simplest.
05:39Now it might look a little strange because to create this NSManagedObject we are using
05:44a factory method but not of the NSManagedObject class, but a different class, NSEntityDescription.
05:49And what we are saying is make an NSManagedObject based on this particular entity, which I can
05:55name with a string here, call it course, and we need this extra piece of information, because
06:00as soon as I create this managed object, and this is true for every managed object, it
06:05needs to be in a container, a named area that's called a context or a ManagedObjectContext.
06:13We'll talk more about what this container exactly is in just a minute. For now, let's just do it.
06:19I have a property of this class called managedObjectContext, and that's what I need here.
06:24So this method that we are using is saying make an NSManagedObject based on this entity
06:29and make it in this container, this ManagedObjectContext, and return me a reference to it.
06:35We now have a managed object. And I want to set the values of this.
06:39Well, in the entity I just made, I had things like title and author and release date, so
06:44it would be nice if I could say something like myMO.title equals, but I don't have that.
06:51Unfortunately, that won't work yet because I created this as a NSManagedObject
06:57reference, and that's a generic class, and it doesn't know about custom properties.
07:02We'll see a little layer how I could get some custom properties and have .title and .author
07:07and .subject or whatever else I had directly accessible.
07:11But in the meantime, what I have to do is use key-value coding to set those properties.
07:16It's a little clunkier, but it will work.
07:18So I am using the SetValue: forKey method, giving it a value such as Core Data for the key, and this is a string, title.
07:36And this refers to the name of the attribute that I defined in the Entity.
07:43Next, I'll set the value for the author attribute, and finally, I'll set a value for the release
07:53date attribute, here this is going to be an NSDate, and right now that's it.
08:04I am going to run this, Run.
08:06I am going to go ahead and when this opens, I am going to click the button and click the
08:11button and click the button.
08:13Well, I shouldn't really expect to see very much as what we are doing, we are doing completely
08:19silently, there's no alerts, no messages, no log messages, and for now that's okay.
08:24So here's the question, is this object being created?
08:28Yes, in fact, every time I click this button I'm creating another one.
08:32Now is it being saved?
08:33Well, no, not yet, but they will be in just a second.
08:39So I have created several objects here, and I'm now going to quit out of this app.
08:44I can either use the menu, or I can just hit Command+Q.
08:50But at that particular moment, when I terminated the application those managed objects were
08:55saved by Core Data. Well, why and how and prove it.
09:01See, there is nothing weird or automatic going on, you see?
09:04In a default Cocoa Application with Core Data when you terminate an app, it's going to call
09:09the usual application terminate event here, we have got applicationShouldTerminate, so
09:15I am going to jump to that method, here we go, and there are some code that's been provided
09:19for us to tell Core Data to save. It's actually this line here.
09:26Now we'll explore this code in depth in just a bit. There is a bunch of code here, most
09:30of it is actually just dealing with generating alert messages if there's a problem.
09:35But I do see the actual word save here, and here is where we are saving those objects.
09:39And Apple have put this code in the applicationShouldTerminate method because that's the way they've been
09:44moving towards over the last few versions of OS X, that if you just quit from an application,
09:49it won't prompt you to save, it will just automatically save your work.
09:53Now we will be able to also choose to save anywhere else we want, anytime we want.
09:58We don't have to wait until the application terminates, it's just already here in the template.
10:03But first, how can I prove this work?
10:05Let me show you where Core Data is going to save data for a Cocoa App.
10:09I am going to open up a Finder window, actually here what I need to do is go to my Go menu
10:14and say Go to folder, go to my home folder Library, and I am going to jump into Application
10:24Support, and here's where it was important that I looked at my com.company as the name
10:30here because this is the Application Support folder that's been created for this application,
10:37com.company.project name, which was SaveDemo.
10:40If I open that up, I can see I have got this SaveDemo.storedata file.
10:46This is what's called the store file or the persistent store file.
10:50Our managed objects have been serialized, encoded, archived, whatever term you want
10:54to use by Core Data into this file. And I can prove that.
11:00You see, in Core Data, the internal structure of this store file can take several different
11:05forms, you can tell Core Data what format you want it to use when saving.
11:09Although, until you have a reason to change it you just leave it alone, leave it at the default.
11:14And in a default Cocoa Application using Core Data, the format of this is actually XML, and that's readable.
11:20So I'm going to Ctrl-click or right-click on this and tell it to open up with--nothing
11:26is showing up right now, so I'm going to select Xcode and click Open.
11:32And this is our persistent store file, or what's inside it.
11:35That doesn't look completely obvious here. We've got some strange names like NSPersistentFramework
11:41and lots of keys, but if I scroll down, this looks like the kind of thing I'd expect to see.
11:47We've got an XML format here of an object of type Course with three attributes inside it
11:53for title, release date, and author, and this has all been taken care of by Core Data simply because
11:59we described those entities.
12:00Now I am not going to change anything about this. I just wanted to prove that it was actually
12:05happening that this information was being saved into our file system because I'd click
12:10that button several times.
12:12Now for an iOS application, the default storage format is actually using SQLite, but it is
12:18still storing a data file to the iOS file system just like this version is.
12:24It's just not as easy to navigate and actually demonstrate that.
12:27Now of course, these values can be read back in, but we are not actually worrying about
12:32that for now. That's going to come in the next phase.
12:35What we need to worry about and understand a little better is the object that we had
12:40to use to make this work, the ManagedObjectContext.
Collapse this transcript
Understanding the managed object context
00:01The Managed Object Context is a vital object in Core Data.
00:05It is the beating heart of Core Data.
00:08The Managed Object Context sits in the middle of everything.
00:11It's not necessarily the most complex object, but it is the most connected, the one that drives everything.
00:16It's the conductor conducting the orchestra, it's the engine of Core Data.
00:20Now earlier, I said that one of the benefits of Core Data is that we don't have to traverse
00:26through all our objects, for the ones we want to save or at least calling save on every single one.
00:32What we do is we take our entire group of related managed objects or object graph and say save.
00:39Well, how we group them is the Managed Object Context, it's a specific type NSManagedObjectContext
00:46that you have available when you link to Core Data.
00:49We put all our managed objects inside this context, and we call save on it.
00:56Because in Core Data, an individual managed object just by itself doesn't have a save method.
01:01A Managed Object Context does, and that's why when we create a new individual
01:07managed object, we always create it inside a context.
01:10If it's not inside a context, it can't do anything, because it's the context this container
01:16that we perform the most fundamental operations on in Core Data.
01:20When we save, we do it on the context, when we fetch data out of the store, we will fetch
01:25directly into a Managed Object Context.
01:29If we want to work with Undo and Redo, we don't call Undo and Redo on individual objects,
01:34we call Undo or Redo on the entire Managed Object Context.
01:38And we do all this so that Core Data can keep track of this object graph and all the multiple
01:44objects in it, and we don't have to.
01:46Now sometimes, you'll hear the analogy of a managed object context is being like a scratch pad or a workbench.
01:55And in some ways that almost trivializes what this container can do, it's worthwhile to
01:59remember those words that a Managed Object Context is a work area.
02:04We can bring objects into it, delete them from it, we can manipulate them, and it will
02:09keep track of all the changes to these objects, but nothing is saved automatically.
02:15It's only when we tell the context to save that it will take these objects and push them
02:19out to the store file.
02:21So while creating the data model, describing our entities is what we need to do up front
02:26using that Data Model Editor we've seen.
02:28When we actually start writing the code in our app, it's the Managed Object Context that
02:33will be the main part of Core Data, we'll be writing code to, we'll be interacting with
02:37to accomplish all the things we want.
02:39So how do we get a managed object context? How do we make it?
02:43Well, as you see in a sample project, when you select that Core Data check box, the code
02:47to create one is written for you.
02:49And most of the code that's added to a Core Data template is to actually correctly create
02:54this Managed Object Context.
02:56And it's not that the code is hugely complex, it's just that we need three things in place here.
03:02First, we need an object to represent the data model that we've written, and we get
03:07that from our data model file, our Entities that we've described, so there will be a little
03:12code in our project that will turn those entities into an object called a Managed Object Model.
03:19And second, the Managed Object Context will need to know where it's supposed to load and
03:23save from, literally, where is the location of that store file, the persistent store?
03:28Well, in Core Data, there's actually another object that handles all the details of that
03:34actual communication, that object is called a Persistent Store Coordinator, and it's that
03:40that will take care of the actual store file, whether that's XML or SQLite or one of the other options.
03:47As a developer, we don't really care about the Persistent Store Coordinator.
03:51We just let it do what it needs to do.
03:53It needs to be there, it's part of the picture, and it needs to now about things like the
03:58Managed Object Model, but it's not very interesting, won't require much in the way of customizing
04:03and most of the time won't require us to do anything at all.
04:07But with these two pieces in place, the Managed Object Model and the Persistent Store Coordinator,
04:13we can create the Managed Object Context.
04:16And it's only now that we have the engine up and running and everything revolves around
04:20the Managed Object Context.
04:23It talks to the Persistent Store Coordinator, the Persistent Store Coordinator knows about
04:28our data model, and it knows where the store file is.
04:31And we drive everything from the context.
04:34And in a sample project, whether iOS or Cocoa, if you've checked that Core Data check box, it's all done.
04:41The extra code is there mainly to set these three things up.
04:44So let's take a look at it.
04:46I've opened up a project in Xcode, the Cocoa application I created a little earlier, doesn't
04:51really matter what you'd be looking at, whether this was a Cocoa app or an iOS app, I just
04:56want one that have that Core Data check box checked.
04:59If I jump into the AppDelegate header file, I'm going to find three properties here for
05:03the three elements I just described, the ManagedObjectModel, the PersistentStoreCoordinator and the ManagedObjectContext.
05:11Because this ManagedObjectContext is almost always wanted across the entire application,
05:17the AppDelegate is a pretty good place for it to be, although it can be useful to pass
05:22a reference to this around into other view controllers, particularly in iOS, just so it's
05:27always convenient to call it from wherever you are.
05:30What we'll find, if I jump across to the implementation for this is there'll be methods to do some
05:36lazy instantiation for these.
05:39So while up at the top of this they're synthesized, if I use my jump bar, I'll be able to jump
05:44and find methods for all of them.
05:46First, I'll jump to the method for the managedObjectModel.
05:50A managedObjectModel is our in-application representation of our data model file.
05:58So what's happening here is it's just first asking, well, do I exist?
06:02If I already exist, just return the object.
06:05If not, the lines 37 and 38 are going out to our project resources and loading in a
06:11file with a project name and extension MOMD.
06:15That's coming from our xcdatamodel file, that's converted into an MOMD file during the build
06:20process, and this is all that does.
06:23Just load that file and convert it into a code representation of our data model, our entities.
06:28Next, after that, we have the second element, the persistentStoreCoordinator.
06:33This has a bit more code.
06:35Because first off, it's looking for a file location, it'll actually be generating a directory if necessary.
06:41Now this code would look a little different in the iOS versus Cocoa templates because
06:46the way of getting to the directories are slightly different, and also, if you scan through
06:50this, you'll find some information about the store type.
06:53Again, in a Cocoa application it's defaulting to the XML format, whereas in iOS, it will be using SQLite.
07:00But you rarely need to add this code yourself, even if you have to add it to an existing
07:05project, you're probably better off copying and pasting most of it from a new project,
07:10it's fairly standard stuff.
07:12And finally, we have the managedObjectContext creation method.
07:18Most of the code in here is actually just checking to make sure that the Persistent
07:21Store Coordinator already exists, and if he doesn't, there's a problem, otherwise we're
07:26doing an alloc init on that and returning that value.
07:30So the creation of the Managed Object Context is actually quite simple, but we need the
07:35other things in place to make it.
07:38And this, in fact, is the majority of code that's added to a Core Data template, everything
07:42else revolves around this.
07:44This is the code you would need in your own projects if you couldn't check that check
07:48box when you originally made it.
07:50So as we now have this Managed Object Context, next, let's see how we can use it to perform
07:56a manual save at the time of our own choosing.
Collapse this transcript
Saving the managed object context
00:00So I'm still in this simple Cocoa Application where I'm creating a new managed object based
00:05on an entity I've described and just setting a few values of it.
00:09I know this is being saved when the application terminates, but let's say I want it to save
00:13immediately as soon as this object is created, rather than waiting for someone to quit.
00:17And all I need to do that is call the save method of the Managed Object Context.
00:24To prepare for that, I need to create an NSError reference here. I'll just call this error.
00:30Though it can be nil, I actually don't need an NSError object, I just need something to
00:35hold one and the pass that into the save method.
00:39Now there is a method in App Delegate itself that returns the Managed Object Context, so
00:46that's the easiest way to call it and then say save and passing in the address of that error pointer.
00:55This would do it here; however, this method, call, the call to save will return a Bool,
01:00so it's going to be yes or no, yes if this is successful, no if it isn't.
01:04So it's very common to actually wrap this in an if statement. Let's quickly do that.
01:10So I'll just grab that here, and we want to test for the negative if there is a problem.
01:21So I'm calling the save method, I am negating it, saying if there is a problem if it doesn't
01:26return yes, I'm going to pop up this NSLog. That's it.
01:30And that's what I mean by saying once you do the setup, once you've done the data model,
01:34once you have got your Managed Object Context up and running, this part is easy and saving
01:39this Managed Object Context, it's no more complicated if you have 1,000 objects.
01:44In fact, anything else we might want to do right now becomes more to do with the user
01:49interface than it is with Core Data.
01:51So, for example, if I look at the sample code in this Cocoa App, the code that was provided
01:57by selecting that check box, I am going to drop into the applicationShouldTerminate section
02:03where we're doing the save here, and that's actually the same call to save.
02:08Most of the code after it is really just presenting model alert information in the case that there was
02:13an error, it's really all to do with the UI here.
02:16However, there are a couple of things before the attempt to save that are worth talking about.
02:22As soon as someone requests to terminate the application we are first asking, well, does
02:26that Manage Object Context even exist?
02:30Remember that we do lazy instantiation, so if you've never tried to do anything with
02:34Core Data there might not be a Managed Object Context object to work with, so if that's
02:39the case, we'll just say NSTerminateNow, go ahead and quit, it's fine.
02:44Now, next on line 165, where we're asking the managedObjectContext to commitEditing and
02:52checking the result of that.
02:53Now this is really only for the Cocoa desktop, folks. This is not something you would see in iOS.
03:00commitEditing is really part of NSEditor, which is associated with controllers and user interface elements.
03:07What this means here is if you're using Cocoa Bindings to bind your managed objects directly
03:13to user interface elements--now we are not doing that yet, but we might--and say the user
03:18is right now editing a text field that's bound to one of these objects, we want to make sure
03:23that those changes are saved.
03:25So it's asking if that's happening it's attempting to commit right now, and if there's a problem
03:29committing those changes, we are actually going to cancel the attempt to terminate and
03:35keep the application running. Again, still all about the user interface here.
03:40Finally, right before the save, we are asking the managedObjectContext, do you have any changes?
03:46Remember that it's keeping track of changes to all the objects inside it, and all we are
03:51saying here is if the call to hasChanges returns no, then there is nothing to save, we don't
03:56need to save, we can just go ahead and terminate.
03:59Right now we can go ahead and quit the application.
04:02And asking if the managedObjectContext hasChanges is useful in the iOS 2 just so you're not
04:08attempting to save if you don't need to, and that's pretty much it.
04:12Now if I go back up and looking at the area where we're creating and setting the properties
04:18of this managed object, well, I did promise that I'd show you a better way than key value
04:23coding that we are having to use here.
04:25So next, we are going to see how to create better, more specific managed objects and
04:29also how to add custom behavior to them.
Collapse this transcript
Creating custom managed object subclasses
00:00So I'm continuing to use this straightforward example that I have where this Cocoa project
00:05has a Course entity with author, releaseDate, and title, and then in my code I've got a method
00:12that instantiates a managed object based on that entity, seta its properties, and saves it.
00:18But I promised there was a better way to do this, and that's by creating a custom class
00:23for this managed object instead of using the generic NSManagedObject pointer.
00:27Now these doesn't mean that for the same idea we need two things, an entity called course
00:33in our data model, and also a custom Objective-C class also called course.
00:39The names don't have to match, but they usually would.
00:41The entity is for Core Data to describe how this managed object is going to be saved, and
00:47the custom class will be for our app to describe our custom behavior for this managed object.
00:52And it's very, very common to have both an entity and a matching custom class.
00:57Now if you're wondering do I need to create custom classes?
01:00No, you don't need to.
01:02If you have an entity that is just a data structure, you don't need a custom class for it.
01:07You can do it just as we've done already. This method will always work.
01:12But if you want to provide custom behavior for your managed objects, yes, you need a custom class.
01:18If you want to use named properties instead of key-value coding, yes, you need a custom
01:23class, and it's very easy to do. Here's how.
01:26Well, I could just go into Xcode and from the File menu add a New > File and do this
01:32myself, but there's a better way, an easier way.
01:35I'm going to jump into the data model, and with that selected I'm going to go to the Editor part
01:39of the menu and come down to this option Create NSManagedObject Subclass.
01:46Generate some Objective-C code based on this entity. We'll click that.
01:53Now if there was more than one entity in my data model I'd get a dialog box here asking
01:57which entities I was interested in, but here we've only got one.
02:01So my next screen is just a save screen, where do you want to save your new class?
02:05This will just be a regular Objective-C class here.
02:08There is an option down here for Use scalar properties for primitive data types.
02:13By default, primitive types defined in your entity will be wrapped in an NSNumber for
02:18your managed object, and usually that's fine.
02:21So unless you have a problem with that, I'd leave this uncheck.
02:24I'll just go ahead and click Create, and we are done.
02:28We have Course.h and Course.m in our project.
02:34Looking at the header file, it's pretty much what you would expect.
02:37We've got properties for the NSString for author and title and an NSDate for releaseDate.
02:43The only difference here from a standard object is that we are inheriting from NSManagedObject
02:50rather than just NSObject, and that's important. This wouldn't work properly otherwise.
02:55Now if I jump over into the implementation, what I can see here is that the properties are using
03:00the at sign dynamic keyword rather than synthesize, and this is the default for managed objects.
03:07It means the property assessors are dynamically generated at runtime, not synthesized at compile time.
03:14Again, that is the default for managed objects. Just leave it at that.
03:18There is one more thing that's been changed.
03:20If I jump back into the data model, what's happened is this entity--I'm going to open
03:25up the Utilities panel here make sure the entity itself is selected--and over here we
03:31have an option called Class.
03:34The Entity in the data model knows that its associated Class is Course.
03:39Now a moment ago--and I can do this just by deleting from class--what the default would
03:44have been was NSManagedObjects.
03:46So unless we say differently, that's what it will be, but we have the custom class.
03:51So what's the difference in code?
03:53But what I can do is that in my AppDelegate now where I'm creating this object what I
03:58can do is have an actual custom course pointer rather than NSManagedObject.
04:03Well, first I need to jump up to the top of this file and do an import--of the course header
04:13file--and jump back into where I'm creating that object.
04:18So instead of NSManagedObject, I'm going to create a pointer to a course object.
04:23Now because I'm still using this same method to create the managed object,
04:29the insertNewObjectForEntityForName: inManagedObjectContext,
04:33that will always return a base type of NSManagedObject.
04:36So I do need to cast that returned object into a course.
04:41So I'll just put a cast in front of it. Cast this to a course pointer.
04:47Now so what's the difference?
04:48Well, instead of having to use key-value coding, I get named properties.
04:53I can type myMO.title, Core Data, author, or of course the sent versions of that, so
05:08myMO setReleaseDate. I don't need the KVC versions.
05:18Of course, one of the benefits here is this is giving me compile time, checking on these
05:23properties, and that's something I wouldn't get with KVC.
05:26So it's much easier with key-value coding to make a mistake and mistype something.
05:31That's nice to have, but what I can also do is add behavior to this custom class.
05:36As soon as it's generated, we can do whatever we want with it, so I can add a little method
05:41here, and in the implementation just provide something simple so we can prove that this works.
05:51I jump back into my AppDelegate, even though I'm creating this as a ManagedObject. I can now call a
05:57method of that myMO SimpleMethod. To prove these work, just go ahead and run it.
06:05Every time I click the button we should be creating an object of that custom class.
06:10It's still a managed object, so Core Data is still handling.
06:14Just making sure I can see that information in my results here.
06:18There we go, Custom behavior, Custom behavior, Custom behavior.
06:21So it's a custom class allowing us to do everything we do normally but which is still a managed
06:26object being saved and managed by Core Data.
06:29I'm going to click out of that and back into the application.
06:31Now one of the things I'm asked is: is there a way to automatically update these class
06:36files if you make at change to the entity? No, it's pretty much one way.
06:41You can always regenerate those class files again from the entity, but it's not going
06:46to pay any attention to changes you've added to the class like Custom behavior.
06:50Now it won't just overwrite them.
06:51It'll warn you if you try and do this, but just be careful and be aware, that is your
06:55responsibility to handle copy any custom data across if you want to regenerate these class files.
07:02Or just manually updating that class later on to match any changes that you might make to the entity.
07:09Because there is no magical connection going on under the hood you're seeing everything here.
07:14All you have here is the .h the .m and the data model that in the Utilities section is
07:21actually pointing over to my custom class. That's it. That's all there is.
07:26The only official link between the two is this class name and the data model.
07:31What's probably becoming apparent is if you are going to use this feature--and it's very welcome to have--
07:36obviously, the entity comes first, and in a production project you'll want to get your
07:41entities as well defined as possible before you generate any custom classes from them,
07:46because there's this no point to generating custom subclasses from an entity if you expect
07:52that 5 minutes later you're going to change the definition of the entity.
07:55If you've already written the classes, you can of course just link them later on duplicating
08:00an entity and changing the class name of that entity yourself.
Collapse this transcript
4. Fetching in Core Data
Creating and using a fetch request
00:01We've see how to model some entities, we've seen how to create managed objects, placing
00:05them in a managed object context and using that to save them.
00:09But being able to create and save objects isn't much use if you can't get them back.
00:13So let's see the third essential piece of the puzzle, how to retrieve--or as we say in
00:17Core Data--how to fetch our objects.
00:21Now everything we do will still revolve around the managed object context, the beating heart of Core Data.
00:26But to fetch you must create a fetch request object.
00:31The fetch request describes what you want.
00:34You don't fetch everything, and it's most basic a fetch request will say that you're
00:38fetching all the objects of a particular entity.
00:42Fetch all courses or fetch all books or fetch all publishers.
00:46Now you can get more specific ordering or sorting the results, so fetch all books sorted
00:51in order of publication date or only fetch specific things.
00:55Just fetch all books with the word apple in the title, but you'll always need the entity.
01:01And to create a fetch request, you really only need two things, the name of the entity, and
01:06the name of the managed object context.
01:09Because just as it's the managed object context that performs a save, it will be the managed
01:13object context that will execute our fetch request and get the objects out of the store if necessary.
01:21So let's go ahead and do this.
01:23So I'm in Xcode. Now, if you've watched any of my other courses you know that I really
01:27like to do demos from scratch where possible.
01:30Here that would be a little long, so I do have a project that I'm going to start from.
01:34Now if you don't have access to the exercise files, or even if you do, let me explain what I've done.
01:39I've made a Cocoa project with Core Data.
01:42In the data model, I've created one entity called Course with author, releaseDate, and title.
01:49And I've generated a custom class for that course, so I can have named properties, but
01:54there's no custom behavior in it.
01:55And if I jump into the main window for this, I have two buttons, and only one of them has
02:02any code behind it.
02:04The Create Managed Objects button is calling a method in my appDelegate called createObjects,
02:13and all this method is doing is seeing the code that we've seen before to create five different course objects.
02:20I need to create these objects just so we have something we can fetch.
02:24So if I scroll down, there's just the details for a few different objects here, and then
02:28we're saving them in our Managed Object Context.
02:31That's it. I don't have any other additions, no other user interface elements, everything
02:36else is the standard Core Data Cocoa application. So let's fetch.
02:41On my window I have a button, French Managed Objects, and this is actually calling a method,
02:46it's already hooked up in the background called fetchObjects, but there's nothing there yet.
02:51So everything we're going to do is in here.
02:54One of the nice things about this is we don't even need to type very much, because Apple
02:58have quite kindly provided us some Code Snippets we can use.
03:02I'm going to just give myself a bit more screen real estate here and open the Utilities panel.
03:08Towards the bottom here, I'm going to click the second icon, the curly braces to give
03:12me my Code Snippet Library.
03:13And because there's quite a few things here, down at the bottom I'm going to filter on
03:17the words Core Data. And the first three are kind of interesting.
03:22I've the Core Data Basic Fetch, Core Data Fetch with a Predicate, Core Data Fetch with
03:27the Sorting, we're using the first one right now.
03:29So all I'm going to do is grab this and drag it over into my fetchObjects method.
03:35I've got a few exclamation marks here just because I need to fill in some details.
03:40On this line 194, we're creating a new fetchRequest object.
03:44I said that it needed two pieces of information, watch the Entity name, and watch the ManagedObjectContext.
03:50So here we've got a couple of placeholders. What is the entity name we're interested in?
03:54We're interested in Course.
03:57Hitting tab, it will jump me to the second part. It needs the ManagedObjectContext.
04:01Well, I can get this by calling the ManagedObjectContext method of self.
04:09That will do there, so we're creating the fetchRequest, defining the entity for it, and
04:15then we're setting that entity as a property of the fetchRequest, simple as that.
04:20Down here we're just about to execute it.
04:22I execute the fetchRequest on the ManagedObjectContext, so this is, again, just a call to self managedObjectContext.
04:32We execute the fetchRequest. If there's any error, we're going to have a problem here.
04:36So we execute the fetchRequest in the same way that we execute a save, and when we do
04:40a fetch we get an array back.
04:43The Error handling code that we've got starting here on line 200 is asking if this fetchedObjects
04:48array is equal to nil.
04:50Now it's perfectly acceptable that we might retrieve an array with zero objects in it.
04:55Maybe there aren't any saved entities, but what we don't expect is a nil array.
05:00So if it's coming back with nil after that execute, we've got a problem.
05:09And I'll just put in a basic message here.
05:11Now we have an issue here on line 204, and that there is a release to the fetchRequest object.
05:16We're using arc so we can't do that, so I'll just delete that line.
05:21And because the indentation isn't very nice, I'm just going to select my code here and
05:25from Editor come down and Re-Indent, there we go.
05:30So this will create the fetchRequest, set the entity, and then execute it on the managed object context.
05:36Well, now what? Well, we should have an array of objects back.
05:40I don't currently have a user interface element to put them in, so I'm just going to log some
05:44messages to prove this fetch worked.
05:47I'm going to do a for statement, but I'll use the Objective C fast enumeration here.
05:52I'm going to go through every course that should be returned in this array.
05:57So Course, and we'll call it c in the array, array is called fetchedObjects.
06:03I'll just write out a simple message and just use a couple of the properties for the purposes of time here.
06:15Okay, let's save that, and I'm going to go ahead and run this code.
06:24Now if I click the button to fetch the managed objects, I wouldn't expect to see any message
06:28because there shouldn't be anything.
06:29But neither would I expect a problem. It's perfectly acceptable for that code to go.
06:35I want to go and fetch all these courses, okay, there aren't any, that's fine,
06:39but what I'm going to do is click the first button which will go ahead and create five of them.
06:44We click that, and then I'll click the second button again, and what I should get, other
06:50messages down here iterating through those Course objects and just writing out the title and the author.
06:58Now I'm going to just clear out of that, jump back to the application, and press that button,
07:03say three times, and notice that if I do press that button, that creates them and press the
07:08Fetch one again, we're going to see them all listed again, in this case we'll have 15 of them.
07:13Because even though they might contain the same values, I'm executing code to instantiate
07:17five managed objects whenever I click that top button, and it will keep adding them to the store.
07:22So even if I clear this out, and now go back and do a basic Fetch, we should expect to see 15 of them.
07:29Now one of the things you'll find is that with a basic fetch you will get all the objects
07:34on this entity type, but they're not guaranteed to be in any particular order.
07:39So next let's see how to fetch them in order.
Collapse this transcript
Ordering with sort descriptors
00:00So when I execute this fetch, I have all these entities being brought back but in a non-specific
00:06order, and to order these in some fashion we use the same fetchRequest. It's exactly like this.
00:11We just add to it.
00:13We add a new very simple object called a sort descriptor in that so all it does is describe
00:19a sort, and to do that it just needs two pieces of information, what attribute are we sorting
00:24on title, author, releaseDate, and do I want that ascending or descending? So let's do that.
00:30After I've created the fetchRequest I'm going to create a new instance of an NSSortDescriptor.
00:41And the init I'm going to use here is the initWithKey: ascending.
00:46The key is just the name of the attribute I want to sort on, which for my case I'll
00:50do releaseDate, and ascending is NO, meaning I'd like that descending.
00:59Order by releaseDate descending, the highest one first.
01:01If I misspelled the attribute name like I nearly did there, you'd find an error as soon
01:06as you try to execute that fetchRequest, but you wouldn't catch it until runtime.
01:10Now we often want to sort by multiple attributes, say first sort by author name ascending, and
01:16then within that by releaseDate descending, so we could do all the different authors and
01:22their courses by the latest one first, and all you do to do that is just create multiple
01:27NSSortDescriptor objects the same way I just did this one.
01:31So you can have a second, a third, a fourth.
01:34Now because the fetchRequest object knows that we could be providing it multiple sort
01:40descriptors, we have to give it an array of sort descriptors, even if there's only one,
01:44so I need to put this in an array. Let's make an array now.
01:51Use initWithObjects and give it the first object, which is the sort I created on the
01:56previous line. That's all I need to do just finish that line.
02:01If I had more sort descriptor objects, I'd just put them in sequence before the nil.
02:05And finally, I'm going to give this array to the fetchRequest, so it's the fetchRequest that
02:10I created up here, making sure I do that before we actually execute it.
02:18And we've got the setSortDescriptors method just passing in the array SortDescriptors, Save and run.
02:29I've already pressed the button to Create these Managed Objects, they're already saved in
02:33my store file, so when I fetch I should get them.
02:35There are a few duplicates in there, but they should all be sort of the same way. We'll take a little look here.
02:42I'm getting them sort in releaseDate descending so that the Core Data Course first and then
02:46the iOS with MapKit and Core Location back to Cocoa, C/C++, and Java was the oldest one,
02:53so that's exactly how I'd expect it.
02:56Just to prove this is working fine, we'll jump through and get rid all these comments here,
03:02and I'll sort it the other way around.
03:04And all I'd need to do here is change the ascending part of my SortDescriptor to YES and run
03:16that again just to prove that it works, and we should see everything the other way around, Java being first.
03:23And that's pretty much it.
03:26If you come from an SQL background, you can pretty much imagine the Sort Descriptor object
03:31as the equivalent of just the order by part of a select statement.
03:35We describe the sort, we add it to our fetchRequest, and we execute the fetchRequest.
03:40But what we're not doing is restricting any of these results, we're just ordering them.
03:45And you're probably already wondering, how do we filter these down, how do we restrict
03:49the entities that we're fetching? And we'll see that next.
Collapse this transcript
Using predicates
00:01So I have code here to create the fetchRequest, to add a sort descriptor object to it and to execute it.
00:07The next step is only fetching specific entities instead of fetching all of them.
00:12So only fetch courses within a certain date range, only fetch employees with the last
00:16name beginning with M, only fetch books where the title includes the word Apple, or only
00:22fetch accounts where the balance is negative.
00:24Now to do this, we use Predicate Objects, and a Predicate will be part of our fetchRequest.
00:31We will write a Predicate and attach it to the fetchRequest just as we created the SortDescriptor
00:36object and attached that to the fetchRequest.
00:40So I am using the NSPredicate class here, and this is not unique to Core Data.
00:45NSPredicate is part of the regular foundation framework, actually as a SortDescriptor that we just used.
00:51And a Predicate is how you describe something that should be true.
00:56You can think of a Predicate almost as an object that just has the condition part of
01:01an if statement or a while statement, well, in fact it can be a lot more complex than
01:05that, but what we're trying to describe is a situation we want to be true.
01:10For those of you with a SQL background, the Predicate is almost like the where part
01:14of your select statement.
01:16So I am going to create one here using the method of NSPredicate called predicateWithFormat.
01:22And what it wants is an NSString.
01:24It wants me to describe what I think should be true.
01:27Well, I am going to say that for now I just want courses where Bill Weinman is the author.
01:33And this is a simple example. We are going to go through a few in the next few minutes here.
01:38So I'll give it a string literal, and this is what I am going to type, author == and
01:44then in single quotes, Bill Weinman.
01:48Because the larger string is in double quotes, I am using single quotes around the string I want to match on.
01:53Now it doesn't actually matter in a Predicate string whether I have a double equals or a
01:58single equals sign, but I like staying in the habit of using double equals for quality.
02:04So that's it, that's the predicate created. Now I need to attach it to the fetchRequest.
02:10So just fetchRequest, setPredicate, and the predicate object we just made, save that,
02:16go ahead and run it. I will click Fetch Manager Objects.
02:20Well, I have a few duplicate objects here because I had clicked that first button a
02:25few times, so I have got a bunch more objects than I actually need, probably about 20 of them.
02:30I don't have to clear them out, but I would actually just like to get to the position
02:34where I have only got one of each. Here is the easiest way to do it for me.
02:37I am just going to delete my store file and let it recreate it.
02:41So I will quit out of that, jump over to my Finder, and then click my Go button and just
02:47go directly to the folder under my home folder called Library/Application Support/, and in
02:53there I should find the entry for this app, and this will be dependent on what my company
02:59entry was for my project, but for me it's com.myCompany.FetchDemo, and I am just going
03:05to go ahead and delete the store file because the code inside this Cocoa application will
03:11actually recreate that store if it doesn't find one.
03:14So let's prove that, go ahead and run it again.
03:17If I click Fetch, I should have nothing, but if I click the first button I will just create
03:21one set of object, and now when I fetch, I get one, that's about right.
03:27So let's make this predicate a little more interesting.
03:30Well, one thing to see is is it case sensitive and absolutely it is.
03:34So if I try and match on lower case, run that again, click Fetch, we have nothing because
03:41the actual value of the attribute had an uppercase B and an uppercase W.
03:45But what we can do is tell this predicate I don't want this equality check to be case sensitive,
03:51and here is how we do that.
03:53After the double equals, I am just going to use square brackets and put in the letter C,
03:56save that and run it.
04:00This time it doesn't matter what the case is of that, it'll match anyway.
04:05Couple of other examples we could do here. Instead of having the full name, I could use
04:11the word LIKE and then have the string but have a wild card. I can use the asterisk for
04:18a wildcard, which means any amount of letters after the word bill.
04:22Again, this is actually case sensitive so just using LIKE and trying it this way should fetch
04:28nothing, but if I added the C after the LIKE and tried it again, we should fetch Bill's course.
04:37Now C isn't the only other word we could use.
04:40For example, if I type in V-E here--so I want anything where it begins with the letters
04:47V-E for an author--run that, and fetch, I get nothing.
04:52But I do have the author of Veronique Brossier, the problem is is that E in Veronique has
04:58the accent over it.
04:59So another thing I could do here is add a D, so make this both insensitive to case and
05:05insensitive to diacritical marks, accents and circumflex and things like that.
05:11We run that again, click Fetch, and we will get Veronique's course here.
05:16Now when you are using LIKE, you can put the wildcards either at the start or at the end
05:22or anywhere in the middle.
05:24So if I do this and put in V-I-D in the middle of it, run that again, we will get David's
05:30course because the V-I-D is somewhere in the middle of that wildcard string.
05:34If you knew that what you wanted was just matching on, say a first name, instead of using
05:38LIKE, you could say BEGINSWITH. Then we don't need the wildcards.
05:46It is still case sensitive. I want to be careful there.
05:49Again, we could make it case-insensitive with the C in the square brackets, try that
05:54again, and we get David Gassner's course.
05:56Technically, you don't have to say BEGINSWITH or LIKE all uppercase, that's just by convention,
06:01the way that it should be done, so it's very recognizable when you're scanning this string.
06:07But of course, we don't just have to work with the Predicate on author, we could do other things too.
06:11Let's work with releaseDate.
06:14Now that won't work really well with a BEGINSWITH.
06:16Let's say we wanted all the courses where the releaseDate is before today's date, because
06:22I know that one of them is after. So I am going to say releaseDate less than.
06:27Well, how do I say the current date?
06:29We can have optional arguments in here, and we do them like a format string.
06:34So I am going to say releaseDate less than, and then I will have percent sign, at sign
06:38like a regular format string and after the closing double quote, a comma, and we
06:43will just create a new NSDate for today, save that.
06:50So give me all the courses where releaseDate is less than today.
06:55We run it, click Fetch, and what I will get here is four courses, but not the Core Data
07:01course, which because I haven't finished recording it is certainly not going to be out today.
07:06Not surprisingly, if I just turn that one around, releaseDate greater than, I should
07:12only expect one being retrieved, which is the Core Data course.
07:16And you can even combine these.
07:19So for example, if I wanted author == David Gassner and the releaseDate is less than today,
07:29we can go ahead, run that, and get pretty much what we would expect coming out of it.
07:34So we have got equality, we've got greater than, less than, greater than, equal to, less
07:38than, or equal to, LIKE, BEGINSWITH, we can also use the word CONTAINS.
07:42We will see a few more Predicate strings as we move through the rest of the course, but
07:47rather than try and commit them all to memory, do know that there is a Predicate programming
07:53guide which you can get either from apple.com, or it's available in your own documentation.
07:59If I jump over here and do a quick search, I will type in predicate programming guide.
08:07I have multiple sets of documentation here, so it might take just a second to come back
08:11and jumping into one of these, we'll talk about the basics of predicates and creating them.
08:16Probably the most interesting thing once you have gotten beyond the first few
08:20is the Predicate Format String Syntax.
08:23And that will kind of give you a summary of what the operators you can use, equals and
08:28double equals, equals to and greater than, the words between, the ANDs and ORs are NOT,
08:35BEGINSWITH, CONTAIN, ENDSWITH, LIKE, MATCHES, if you want to use regular expressions.
08:40So one of the things you could do is bookmark the Predicate Programming Guide while you're
08:44getting used to writing your first few predicates.
08:47And also, one last thing to point out with this is know that one of the built-in code
08:54snippets is a fetchRequest with a predicate.
08:58So if I jump over into my Code Snippet library, filter down on Core Data, we have got the
09:04Core Data Basic Fetch, we have seen that, Core Data Fetch with Sorting, we've done that,
09:09there is also Core Data Fetch with a Predicate that could be just dragged in and everything is there.
09:14This is pretty much identical to what we have actually just done, but it's a quick shortcut to get to this.
09:19I'm going to go ahead and delete that because all my code here works just fine.
09:24But if you know that you have got to create a few complex predicates, there is another
09:28way to do it, where we can do it visually, I will show that next.
Collapse this transcript
Creating fetch request templates
00:00If you know that what you'll need to do in your application is create a whole bunch of
00:05fetchRequests together with predicates,
00:08one option that you have is that instead of writing this in code you can predefine these
00:12in your data model file using the Model Editor and save them as what are called Fetch Request templates.
00:19Let me show you what I mean.
00:20Say that I knew one thing I'd have to do in my application is fetch all the courses
00:26that have the word essential in them--so fetching in my case, all the essential training courses or iOS essentials--
00:33what I could do is go into this Data Model file.
00:37Now under the Entities section, I can see there is a Fetch Requests section.
00:41Nothing is here right now, but I can add something.
00:44Either from the Editor menu we have got an Add Fetch Request or from the button down
00:49at the bottom that currently says Add Entity but there is a little arrow there that I can
00:53hold down and click and say Add Fetch Request.
00:56I actually prefer using the Editor menu because when you select another option from the button
01:01it will actually change the default behavior of the button.
01:05So Editor > Add Fetch Request gives it the default name of FetchRequest. I'm going to
01:10change this to let's say EssentialCourses.
01:14Now this name is what we're going to use in code to grab this predefined Fetch Request.
01:22And over on the right-hand side we have the way to define it in this visual editor.
01:27First, we have to provide it what entity we are talking about.
01:30Well, in my data model it's pretty simple.
01:32So I only have the Course Entity, and below that I have the predicate editor.
01:38So where all of the following steps are true, we click the little Plus sign here, and what
01:42I do is have a dropdown list of my attributes, again very simple data model here, but I could
01:47say title, contains, or is, is not, begins with, ends with.
01:52Let's say contains Essential.
01:55I could click the Plus button to add more things to check on, but I don't need anything
02:00other than that just a title. Contains Essential is good enough.
02:04This is a normal contains which means it is actually case-sensitive.
02:09One way I can change this is I can click this button up on the top right section, which
02:14takes us between this Visual Editor view and just a view of what the predicate string would
02:19be if you manually typed it yourself.
02:21And you can change it here if you wanted to. I could add the C in square brackets to make
02:26it case insensitive. I am just going to leave it as is.
02:30So I'll save that. That's my Fetch Request template predefined, so how do I get to it in code?
02:36So I am jumping over into my AppDelegate.
02:38I'm going to use the same place that I had my previous code to compare and contrast here.
02:45See what I won't need is all of this stuff up here, and I won't need this predicate either,
02:52I'll delete that in a moment.
02:53But for now, what I'm going to do is create the Fetch Request from that template.
02:58The way that I get to that Fetch Request template is by thinking about, well, where is it?
03:04That is stored in our Data Model.
03:07So I need to ask the model for it, and if you remember that model is converted into
03:12an object called a managedObjectModel in our code.
03:16So I can get to that by just calling self managedObjectModel, not Context. Here I am
03:23asking the data model, and there is an option in there called fetchRequestTemplateForName.
03:31That's the one that we want, and I called it EssentialCourses.
03:39That's going to grab the Fetch Request that already has the predicate defined in it, already
03:43has the entity defined in it.
03:45So I don't need this code to deal with the entity, and I don't need this code to deal with the Predicate.
03:51I could still apply a Sort Descriptor if I wanted to, again, we are still working with
03:55the fetchRequest with the same name.
03:58But I don't have to do that either. I'm going to leave it there, and we execute it the same way we always did.
04:03So if I go ahead and run this, we should now just retrieve the courses with the word Essential
04:09in them, and we do indeed, Java Essential Training, C++ Essential Training, and Cocoa Essential Training.
04:16And it makes our code a little bit more compact.
04:20So predefining your Fetch Request is not necessary, but it can be useful and helpful.
04:26They can also be predefined with placeholders for variables, and that we are not going do
04:30that right now. We'll see it a little later on in the course.
04:33But what we do have is the basics of modeling, of creating managed objects, of saving our
04:40managedObjectContext, of fetching, of predicates, and probably the most apparent missing piece
04:45right now is tying these objects to some kind of user interface in a meaningful fashion.
04:50Now here is the thing.
04:52While everything we've done so far is pretty much generic across both Cocoa desktop and iOS development,
04:58when we start connecting these objects to a user interface, we are going to have to
05:02approach things a little differently for a couple of reasons.
05:05One because, say on Cocoa Desktop development we have Cocoa Bindings which isn't available
05:10on iOS--at least not right now--and two, they just have significantly different user interface elements and standards.
05:17So in the next two sections what we are going to do is take what we've done so far, all
05:21the different stages, tying it all together and tying it to a user interface, first working
05:27through an iOS example and then with regular Cocoa. We are going to see some other things.
05:32You are probably wondering about right now, how do you delete objects, how do you edit them?
05:36Let's see that next.
Collapse this transcript
5. Putting It Together: iOS
Creating the project and the data model
00:00Let's take these core ideas of modeling, saving, fetching, and put them together inside a user interface.
00:06Now first, we are going to do this with iOS.
00:09We will create an app to show a list of courses, showing these in a Table View grouped by author.
00:14I can select each one to show the details of that one, but we also have a view controller
00:19where we can add a New Course to either cancel or save that.
00:26I can select an existing course, make a quick change to some of those terms if I wanted to,
00:32or from that initial list, I can delete one and everything will be persisted to Core Data.
00:42While this might not look so drastically different from the example I showed with the timestamp,
00:47we are going to do this completely from the ground up.
00:49We are not going to use the Master-Detail project. We are going to do this from an empty iOS project.
00:55So while we will let Xcode provide a little bit of the standard Core Data code to set
00:59up the Managed Object Context, everything else, including the entire user interface
01:04we will do ourselves.
01:06Now it'll be a little tough to do this in one go,
01:08so I'll go through in several shorter pieces.
01:11Part one, we start at the beginning, create the project, write the data model, set up
01:15the first part of the interface, that Table View that will show everything.
01:19In part two we will add code to fetch into this Table View.
01:22We will see a new object which will help us connect our fetch results into an iOS table view controller.
01:29Next, from the Table View, we will see how to add a plus button, create a new object
01:34using a new view controller, and how to format that.
01:38After that, we'll see how to delete directly from the Table View,
01:42then connect and configure a detail view to show individual details and finally shift
01:47the screen into an alternate mode to let us edit an existing entry.
01:52So classic CRUD: Create, Read, Update, Delete, all done from the UI, and along the way, we
01:58will see a few things like how to make sure your Table View is always being updated in
02:02line with any changes to the underlying objects, how to create default values for your objects, and so on.
02:08This will be a great foundation for adding some more advanced features like undo, redo, and validation.
02:14Now if you are planning to follow along in Xcode, have your pause button handy.
02:18There are a lot of steps and a decent amount of code.
02:21I will explain everything on adding, but consider just watching straight through once to get
02:26the hang of what's going on before attempting to duplicate everything. So let's begin.
02:30I am going to open up Xcode and create a new iOS project, an empty project but with Core Data support.
02:38So selecting empty application, click Next, I'll call this CDCourses for Core Data Courses.
02:45You can, of course, call it whatever you like.
02:47I will make sure I am doing it just for the iPhone and that Use Core Data and Use Automatic
02:52Reference Counting are checked, Unit Tests is unchecked, as is the local git repository.
02:59And I'll just save this to my Desktop.
03:02So if I take a look over here in the Navigator on the left, we can see I don't really have
03:06a lot of things here.
03:07There is the standard code added to the AppDelegate that's preparing our three essential
03:13Core Data objects, the ManagedObjectModel, the PersistentStoreCoordinator, and of course
03:19the ManagedObjectContext.
03:21But when creating an empty application, I have no user interface at all.
03:26So there's no storyboard, no xib, no view controller classes, that's all up to us, but
03:31first let's do the data model.
03:34This file is completely empty. So I have to add the first entity.
03:37I'll just add one called Course, and I will give it three simple attributes, title, author,
03:47and releaseDate, and not surprisingly, this will be author of a String, releaseDate is
03:55a Date, and title is a String.
03:58Optionally, at this point I could also create some default values for this.
04:02So by selecting title, I can go over here and say the Default Value is, and I'll put
04:07TITLE in uppercase just so it will be obvious when we are using it later, same with AUTHOR.
04:15Now if I am setting a Default Value for releaseDate, I do have a place I can type one in here,
04:19but I'd have to really type a literal value, a particular date, and that might not be what I want to do.
04:26A little later on I'll show you how you can actually do your own default values in code.
04:32For now I'll do a basic date, 2012-01-01. So this is a purposefully simple entity.
04:40We are going to see how to hook up these three attributes up to a user interface.
04:43It doesn't really add anything just if I had 10 attributes.
04:47What I'm going to do is generate a class from this.
04:50It's not going to add any custom behavior right now.
04:52I just want some named properties.
04:54So from the Editor, I'll say to Create NSManagedObject Subclass.
04:57You want to make sure of course that your attributes are named correctly and they have correct values.
05:04I want to do this for Course and save these into my project, click Create.
05:10So I have Course.h and Course.m giving me my named properties.
05:15Let's begin a user interface. I'm going to do this with Storyboard.
05:18So from the File menu, I'll add a new file.
05:21This will be a Storyboard which you will find under the User Interface section of iOS. Click Next.
05:28This will be a Storyboard for the iPhone, and I'll call this MainStoryboard, click Create.
05:35A storyboard gets created with nothing on it at all.
05:38It's a completely blank canvas.
05:39So I'm going to make sure my Utility section is open, switch to the Object Library, and
05:45I'm going to drag on a Table View Controller, because I know that the first thing I want
05:49to see is a list of different options.
05:52We will need a lot more than this, but we need something to begin with.
05:56But if I add a Storyboard to my project, it's going to be vital that I tell this project's
06:01Info.plist file about its existence.
06:04So in my Supporting Files section, I'll drop into the Info.plist for this, and I need to
06:10add a new entry to say what the MainStoryboard is.
06:13There are multiple ways I can do it.
06:15One of the easiest, go into the Editor section, make sure I click in there in the middle section,
06:20and then say Add Item. I do have the pop-up here.
06:23What I am looking for is Main storyboard, and if I just type the lowercase m, it won't find it.
06:28But if I type Shift+M for the uppercase one, there we go.
06:32I'm looking for Main storyboard file base name. There are a few options there.
06:37What you'll find is one is specifically for iPhone, the other for iPad.
06:41I just want the generic one, Main storyboard file base name.
06:45This is going to be a String. That's correct.
06:47I'll double-click over here, and we give it the name of the file I just created--which was MainStoryboard--
06:55now there is a storyboard named in our Info.plist file.
06:59This will be loaded in automatically.
07:02So what I can actually do is go into my AppDelegate and remove some code, because right now AppDelegate.m
07:08in the application did finish launching with options has a few lines here to just create
07:13a window object and set its background color, make it visible.
07:17I don't need any of this because that Storyboard is going to be loaded.
07:21So I am deleting everything apart from the words return YES.
07:25I'm going to save this and just go ahead and run it to see if I can have the telltale lines
07:32of the Table View in my Storyboard. Okay, it looks good.
07:35It's giving me a warning over there, but we will take care of that in a moment.
07:38I can tell that my Storyboard is loading, and it's loading the Table View Controller.
07:43Quit out of that and back into the application.
07:46See, we will need a custom class for this Table View Controller as this is going to
07:52do most of the work, and we need a place to write some custom code for it.
07:56So I will add a new file.
07:59Under Cocoa Touch, select Objective-C class.
08:03I'm going to create a class called CoursesTableViewController,
08:07and it's very important that this is a subclass of UITableViewController, not just UIViewController.
08:13I don't need either of these checked, because everything is in my Storyboard, and it's an
08:17iPhone one we are using, and click Next.
08:21And yes, of course, I want to add this to my project.
08:24As soon as I've done that, I'm going to jump back into the storyboard, because right now
08:28this is an instance of the generic UITableViewController, and we need it to be an instance of our new subclass.
08:36So down in the bottom bar, I will select where it says Table View Controller and open up
08:41my Utilities Inspector, making sure that's selected in the third part of the Inspector
08:49here where we've got the Identity Inspector. I am going to select the dropdown, and I should
08:54see legitimate classes pop up, including this new one the CoursesTableViewController. Save that.
09:02I'm still expecting a couple of warnings that we've got, potentially incomplete method implementation
09:07just because we are not actually providing any data that are this table view.
09:11That's to be expected because it's what we are going to do in the next section.
09:16The other warning that I am getting is on the storyboard here that Prototype table cells
09:20must have a reuse identifier. Well, I can go ahead and do that now too.
09:24So selecting the cell itself, coming over into the Attributes Inspector, the fourth
09:30one, again I've got to be careful what I've selected.
09:32We will give it a Reuse Identifier of Cell with an uppercase C.
09:38Now the last thing I'm going to do before we go ahead into the next section is a bit of prep work.
09:45See, if I jump over into the AppDelegate, currently the AppDelegate has a property for
09:50the ManagedObjectContext, the beating heart of Core Data, the one we will need to use a lot.
09:58AppDelegate is a good place for this object, but we will also need to get to this repeatedly
10:02from that new Table View Controller class so we can call save and call fetch and so on.
10:09The most convenient thing to do is that in our new Table View Controller I am going to
10:13add a property to hold a reference to this.
10:16So jump over to the header file and in here just add a property, so a nonatomic, strong
10:23property of type NSManagedObjectContext, I will call it managedObjectContext.
10:30I don't need to synthesize for this because I am not creating any custom accessors.
10:34I will just let Xcode synthesize it.
10:36Now back over in the AppDelegate, what I'm going to do is pass over a copy of that.
10:42So jump into the AppDelegate.h and just do an import so it knows about the new class
10:47that I recently created, CoursesTableViewController, and then jumping over to the implementation.
10:55I'm interested in the application didFinishLaunching section, and what I want to do is grab hold
11:00of that new Table View Controller so I can access its properties and pass it a reference
11:05to the ManagedObjectContext.
11:06So I am grabbing it from the AppDelegate's self.window.rootViewController and casting
11:13it as an instance of CoursesTableViewController so I could then quite quickly go ahead and
11:20set its property, the one we just created of ManagedObjectContext = self.managedObjectContext,
11:27the one in the AppDelegate.
11:29Now this will work fine right now because we only have one view controller in our Storyboard.
11:35If I were to embed that in a Navigation Controller, I'd have to revisit this code.
11:39Well, while I'm thinking about it, I know that I will need a navigation controller,
11:43because we are going to go from that table view to a detail view to add screen to an edit screen.
11:48So let's just do that prep now.
11:50I am going to jump into the Storyboard, select the Table View Controller, easiest way to
11:55click down here in the bar at the bottom to select this object.
12:00Sometimes that's a little difficult if you're not in at the 100% view.
12:04So you can also use the document area here.
12:10With that selected, go up to Editor, click Embed In > Navigation Controller.
12:17What that will automatically do is create the Navigation Controller, set it as the initial
12:21view controller, and nest ours inside it.
12:25It's also given me the start of the navigation bar at the top here, which is fine, it's convenient.
12:30I am going to just double-click that and rename it to Courses.
12:33I don't have to do that, but it's quite conventional to do so.
12:37Then jump back into the AppDelegate, because this code that I just added is no longer going
12:42to work, because we will not be the rootViewController right now,
12:47I'll add a little code to grab it.
12:53So we are using the same kind of code, but this time to grab the UINavigationController
12:57that's at the top, and then what I can do in the next line, I'll still need to cast to
13:02a CoursesTableViewController, I'll change it to access this new object called nav, there
13:09we go, space, it will have a collection of view controllers, and I'm interested in whatever
13:15the first one is objectAtIndex:0.
13:18It might be little difficult to read just because I have restricted screen real estate
13:24here, but hopefully this'll make it a little bit clearer.
13:27Line 19, grab the Navigation Controller, at line 20 we grab our CoursesTableViewController
13:32out of it, whatever is at position 0 and then line 21, we pass a reference to ManagedObjectContext,
13:38and that's all the prep I want to do for now.
13:40Next, let's see how to fetch into this Table View Controller.
Collapse this transcript
Configuring the fetched results controller
00:00When the application loads, and this Table View Controller appears, we want it to fetch
00:04all our course entities out of the Core Data store.
00:08Now I realize we have nothing to fetch yet, but we know we will, so let's do the groundwork.
00:12I am making the assumption that you're already reasonably familiar with iOS Table Views,
00:17that they can be split into sections that we have certain named methods to use to fuel them with data.
00:23If that's not the case, take a look at the Table View chapter of iOS SDK Essential Training first.
00:29So we're going to fuel this Table View Controller, and that means pretty much everything that
00:34we do will be part of the CoursesTableViewController class here.
00:39And this is where we want to execute a fetch out of Core Data.
00:42Now you saw in the section on Core Data Fetching that when you execute a fetched request, what
00:48you get back is an array of managed objects.
00:51Now an array is fine, and that can be useful, but manually writing code to connect an array
00:56to a UITableViewController is kind of tedious.
01:00So we're going to use a brand-new additional object, the NSFetchedResultsController, and
01:06I'll just write a property for that.
01:13Now make no mistake, we still use standard Core Data Fetched Request. We still use standard
01:18iOS Table Views and Table View Controllers.
01:21This object will make it much easier to tie the two together.
01:25The entire existence of the NSFetchedResultsController class is to tie Core Data Fetched Request
01:33to UITableViews in iOS.
01:35So that's in my header file. I am going to jump across into my implementation and add
01:39a synthesize statement here.
01:41While I don't always have to do synthesize statements anymore, I am going to do one this
01:46time because I want to use the underscore format for the internal instance variable
01:52here, and it's also because I'm going to write my own lazy instantiation method to create
01:58this fetchedResultsController object and create it the right way.
02:01Now I can put that method anywhere in this class. I'll put it down towards the bottom
02:06here, let's say just above this lowest Table view delegate section, and it's a good idea
02:12to put in a couple of pragma marks.
02:13If you don't use pragma marks, they just allow us to create our own area in the jump bar,
02:20so we can have a section here. I'll just call it the Fetched Results Controller section.
02:27And that means any methods that I put down here will actually appear in this section
02:31here in the jump bar, making it easier to get around. It has no impact on actual behavior
02:36of the application, you don't have to do it,
02:39but what I do have to have is a method that will return an instance of NSFetchedResultsController
02:45that's obviously a pointer, and the method is called fetchedResultsController.
02:52So the first code, ask is there anything already in that instance variable?
02:57If it's not equal to nil, there is something there, we have a fetchedResultsController,
03:01just return it, we're done.
03:03But if we get past that closing brace of the if statements and we don't have one, we're going
03:07to have to make one.
03:09So the question is, how do you make a fetchedResultsController?
03:11Well, we saw how to make a fetched request, and in fact, most of it is identical to that,.
03:17We actually need a fetched request and a sort descriptor to create a fetched results controller.
03:23So actually, to save us a little time, while I could write it out in full, what I am going
03:27to do is grab my Code Snippet Library, filter on fetch, and I'm going to find not the Basic
03:37Fetch, not the Predicate one. I don't need a Predicate here. What I do need is Core
03:41Data Fetch with Sorting, so I am going to drag that over after the if statement and
03:47get rid of the memory management code. I don't need to release anything. I have arc turned on.
03:52And I actually don't need the part that executes that. What I really just need is the FetchRequest,
03:57EntityDescription, and the SortDescriptor section.
04:01So I've got all of that. That will work.
04:03So I've got my FetchRequest here. What entity is this for? It's going to be for Course.
04:08What ManagedObjectContext do I have? Well, that's a property of self, so I'll just say
04:12self managedObjectContext.
04:14Don't have to use the square brackets, could have just said self.managedObjectContext,
04:18that would have worked.
04:19I do have to have a sort to retrieve these in a particular order.
04:22I am just going to sort on author name.
04:25So the attribute is author, however it's defined in your data model. Mine is, of course, lowercase.
04:31And let me just balance out this code here, go to the Editor menu, and just say Re-Indent.
04:37So I now have my fetchRequest object, my entity description, my sortDescriptor all connected.
04:44So instead of executing this fetch directly, what I'm going to do is use it to create this
04:49fetchedResultsController, this new object that we're going to tie to the Table View.
04:54So this is the new part. We're using that instance variable _fetchedResultsController.
05:01It has a fairly long initializer called initWithFetchRequest:managedObjectContext:
05:09sectionNameKeyPath:cacheName. Well, a couple of these are pretty obvious.
05:14We need to initialize this with a fetchRequest. Well, coincidently we just made one, so we
05:20have a fetchRequest that was allocated on line 121 just called fetchRequest, and that will do.
05:27Next one, managedObjectContext, well, again it's a property of self, so either using the
05:31square brackets or self.manage dObjectContext, we have that one.
05:36Then we have this sectionNameKeyPath, now what does this mean?
05:40The impact of setting this is that we can fetch our results back in sections, and we're
05:45talking about sections here in an iOS Table View, the way that it can be automatically
05:51split up into sections.
05:53If I want that to happen, say that I want to split the authors into sections based on
05:57the author names, so all the courses for a particular author show up in their own section,
06:02then I just name the attribute we want to split into sections here, and it will be author.
06:07I don't have to do that. I could just say nil, and that would bring them back in just one long list.
06:12And finally, cacheName. If we had an awful lot of data, we'd be playing around with this.
06:17I'm going to leave it for now. We don't need that, so I'll just set that to nil.
06:21And that is our fetchedResultsController object created.
06:24Now because this is just an accessor method that needs to return it, I need to have a return statement.
06:29Now you might be wondering, well, did that execute it just by creating it?
06:34No, this is just an accessor method. This is just for anyone that wants to ask for the
06:39fetchedResultsController property. It will create it when needed.
06:42We're going to execute this in a little while, but not yet.
06:46But that's that method done.
06:47Now what I can see up here at the top is I still have the exclamation mark letting me
06:51know there's a couple of warnings here for potentially incomplete method implementation.
06:57So let's take care of those now. We are still in CoursesTableViewController.
07:01The first one is the numberOfSectionsInTableView method, how many sections does this Table View have?
07:07And of course we have no idea. It will differ depending on how many objects
07:11we have, how many authors that we have.
07:14But it's fine. What we can do is ask the fetchedResultsController how many sections do you have?
07:20So here, all I am going to do is return the sections property which is an array and has a count.
07:27Well, that's it. I can get rid of the warning now.
07:31That line will tell the Table View how many sections are in it.
07:34Well, onto the next one, the classic tableView:numberOfRowsInSection.
07:38We'll need one more line, so we'll make it 2 this time, NSFetchedResultsSectionInfo secInfo objectAtIndex.
07:54So what's going on here? It might look a little odd, but line 61 is saying we are working
07:58with collections of collections here.
08:00What we're doing is we're asking the fetchedResultsController for its sections array, and we're going into
08:06a particular one based on the objectAtIndex section parameter, and again, this is being
08:12passed in, this will be called automatically for every single section, so we'll always
08:16have zero, then one, then two, then three.
08:19So we'll find that information, and what we're doing is we're taking the results and putting
08:23in an ID called secInfo, and that has to comply with the NSFetchedResultsSectionInfo protocol,
08:31which is why it's in the angle brackets here.
08:33Because I don't really care about what this is, I just need to make sure that it will respond
08:39to me asking it how many objects do you have?
08:42So in this case, we'll ask what are your number of objects? We'll return that, done.
08:47Now we'll go on to the next one.
08:48This isn't giving us a warning, but we need to configure.
08:51This is, perhaps, the most classic method that we need for a tableView, the tableView:cellForRowAtIndexPath,
08:58meaning what exactly are the contents of each row.
09:02So the first two lines are trying to de-queue a re-usable cell, and after this we're going to configure it.
09:08So I am going to configure the cell based on objects in the fetchedResultsController,
09:12this should be course objects there.
09:14So I'll create a temporary course object pointer, and that's not knowing what I'm talking about
09:19because I haven't imported my course header file.
09:22So let me just zoom up to the top and add an import statement for that, just for Course.h,
09:31use the jump bar and jump back down into cellForRowAtIndexPath and just ask the fetchedResultsController
09:40what's at this particular indexPath right now.
09:42And of course, this method is being called repeatedly for every row in every section
09:48passing in the indexPath.
09:50That will give me the course object for this particular row, and I can just start to configure
09:56the individual cells here, so cell textLabel.text. We're just going for basics here, equals course.title.
10:03I am just going to double-check something here because this is attempting to de-queue
10:09a cell with the word Cell with an uppercase C as the Reuse Identifier.
10:15Let's just double-check that is the case in our Storyboard, so I am going to just jump
10:19across here, look at the Storyboard, select the Table View cell, and bringing up the Attributes
10:26Inspector, yes indeed, my Identifier is Cell with an uppercase C, so they do match.
10:34Back across, and that will work.
10:37Let's delete a couple of the spare lines. All right!
10:42These are all the methods that I have to have.
10:44How many sections do I have, how many rows are in each section, what's the contents of each row?
10:48I am going to add one more thing. This is optional, but seeing as I am fetching the
10:53results in sections, I'm going to add a little code to tell the Table View what to expect
10:57at the top of each section, what title to use.
11:01This is going to be a method that returns an NSString, which is obviously a pointer,
11:06and the method is called tableView, there we go. We should expect it to pop up, tableView:titleForHeaderInSection.
11:15There are some very similarly named ones. We want titleForHeaderInSection.
11:18Again, as soon as I have this method, it will be called automatically, because we are a
11:23delegate and a data source for the tableView.
11:25And what we want to do is jump into the fetchedResultsController into the right section, into the right object, and get its name.
11:34I'll just compress this as much as I can here, go into our array of sections, find the objectAtIndex
11:44called section that's the parameter that's being passed into this method, and find its
11:49property called name and return that.
11:54And the last thing we need to do, even if I've got everything hooked up is we are never executing
11:58the fetchRequest, we're never getting anything in that fetchedResultsController.
12:03So we need to execute or perform the fetch.
12:07Well, most convenient place for me here will be to jump up to the viewDidLoad section and do it there.
12:12So in viewDidLoad right before the end, we perform a fetch quite similar to performing
12:18a save on managedObjectContext, meaning I am going to create an NSError pointer and
12:23pass that in and let it tell us if anything wrong happened.
12:32So create an NSError pointer and then call the fetchedResultsController performFetch
12:38method passing in the address of that error object.
12:42That will return yes if it's successful, no if it isn't, so I'm negating the check.
12:47So if it's not yes, then we're going to pop up an NSLog and an abort message.
12:52I am going to save and build that and go ahead and run just to see if it will give us any errors in the code.
13:01Everything seems to load. I don't see any diagnostic messages popping up.
13:05But of course, here's the problem: we have no actual data to fetch, so we'll fix that in the next step.
Collapse this transcript
Creating and configuring the modal view controller
00:00So next, I'm going to go into the Storyboard, and we're going to add a new view controller
00:05to this that we'll configure as a at course screen.
00:09So with the Storyboard selected, I am going to open up my Object Library, and I want to
00:14drag on a normal View Controller, not a Table View Controller--we could do that, but normal
00:19will work just fine.
00:19I am going to shrink mine down a bit because I can't really see very much here.
00:24So we have a View Controller, now the question is how does this open up?
00:28Well, we'll need to do it from that first main Table View Controller, because that's
00:33what's going to appear as soon as we open the app.
00:36So what I am going to do--let's zoom back in to do it--is I am going to add a button
00:40up here on the navigation bar, just a plus button.
00:44So in my Object Library I'll filter down by Bar button Item, that will do, drop it up here,
00:52and I'm going to change that to the Add sign. That will do.
00:57I want this to open up this new View Controller so what I am going to do is Ctrl-drag from
01:02the button--always be careful on what you have selected. It should be highlighted down
01:05onto the new View Controller. What kind of Segue it asks.
01:09It could be push, could be modal, could be custom.
01:12A push segue would be the easiest here, but let's do this right.
01:17For adding the recommendations, usually a modal segue from the bottom up, which means we won't
01:23automatically be embedded in the navigation bar so that doesn't appear here at the top,
01:27so I'm going to have to do a little bit of user interface work here.
01:31First, I am actually going to add a Nav Bar because that's the kind of look and feel that
01:35I want, so I'll drag that on, double-click it to re-title New Course,
01:43and then I'll add two bar buttons items onto this, one on the left, and one on the right.
01:51The left one I'll select, I want that one to be Cancel, which is a pretty standard one,
01:56and the right one to be Save, which is also fairly standard.
02:01And then back into my regular Object Library, what I want to do is add a few Labels and Text Fields here.
02:08I am going to highlight all these text fields and actually change them to a borderless border style.
02:18Don't have to do that, but why not?
02:21And then check them to make sure that they Clear when editing begins, because I'm going
02:25to be populating them with default information.
02:28Again, don't have to do that, that's just optional.
02:31Now I hope you'll forgive me in this case to have a complete UI but not a polished one.
02:35I would normally associate Release Date with a date picker, but for the purposes of this
02:40demo, I am just going to leave it as a normal text field, and I'll leave the date picker
02:44idea as an exercise for you.
02:45Now I do want these defined as outlets, so what I'm going to do is switch into assistant
02:50view and then click and drag over here. Let's make sure the right one is selected. At the moment
02:57I don't have a custom class, I really need one.
03:00Now that would be a big problem trying to click and drag from this.
03:04So, back over here in Xcode, I am going to add a new file. We want to write some custom
03:09behavior for this View Controller, so we'll add a new Objective-C class, I'll call it
03:16AddCourseViewController, I want to make doubly sure that this is not UITableViewController,
03:22it's just a subclass of UIViewController, both of these should be unchecked, and I'll create it.
03:30The last thing that I'll need to do to make sure everything hooks up correctly is to select
03:35this View Controller and make sure that in the Identity Inspector--again, it's lost it.
03:43It thinks I've got in a text field here, so select the View Controller.
03:46Right now it's the generic class UIViewController. I need it to be AddCourseViewController.
03:51Without this, I'm not going to have any of my custom code working.
03:55And back again, what I wanted to do here was switch into Assistant View, so hopefully this
04:00is going to be the right one now. With this selected, we have the AddCourseViewController.
04:04That looks right, and what I'll do is select the first one, the Title text field, Ctrl-drag
04:10that into the interface, set that up as an outlet called titleField,
04:16take the second one, Ctrl-drag in there, and not surprisingly this will be authorField,
04:22and the third one for date, and I think you know where I'm going with this, dateField.
04:27Well, before we go much further, what I am going to do is just go ahead and run and make
04:32sure the segue is working, and there is no issues right now.
04:35If I click that plus button, we should segue up from the bottom.
04:39So the idea is when we do this, we're going to create a new course object back on that
04:44TableViewController, and we'll pass it in to this view controller.
04:48If they manipulate it and hit Save, we will go ahead and save that new object.
04:52If they hit Cancel, well, we've got an object we don't want, so we'll go ahead and dispose of it.
04:57But neither of these buttons are going to do anything just yet, and we need to make sure that they do.
05:03So back into the Storyboard, I'm going to switch again into Assistant mode and Ctrl-drag first
05:09from the Cancel button into my associated header file, making sure this is not an Outlet
05:14but an Action, and I'll just call this Cancel.
05:18And again, drag from Save, again making sure this is not an Outlet, but an Action.
05:26We all accidentally create Outlets for buttons where we don't want to, but I am trying to
05:30avoid that here, and we'll call that Save.
05:33The idea being that if I jump into my implementation file for that, that I should now have a couple
05:40of methods down here for Cancel and Save.
05:47So we're going to there dismiss the modal view controller and remove the object, whereas
05:51when I hit Save, we still want to dismiss the model view controller, but we want to save
05:56the context. Again, we save the context rather than the object.
06:00But here is the thing, so how do we go ahead and dismiss, we saw that it isn't doing it yet?
06:04Well, dismissing modal view controllers is kind of a pain in iOS. You don't do it directly.
06:10This model view controller shouldn't dismiss itself, it should be done with delegation
06:14so that the same view controller that was responsible for popping it up, which is that
06:19CoursesTableViewController is also going to be the one that's responsible for dismissing it.
06:25So we need to do a little prep in this new AddCourseViewController to allow that to happen.
06:30Well, first, I know that I'm going to be passing in a course object, so I am going to just
06:35jump over into my AddCourseViewController header file here.
06:39And if I am going to have a course object, I better do an import statement.
06:42And I am also going to create a property for that course object, just going to be one at a time, so I'll call it current course.
06:54However, the main thing that we need to do here--which is not specifically about Core Data,
06:58just about the way that modal view controllers should be done in general--is we're going to
07:03create this new course view controller as having its own delegate protocol.
07:08Now I am going to actually declare it up before the word @interface, so here I'll put protocol,
07:14and usually you just name it after this class, so we'll call it AddCourseViewControllerDelegate,
07:22and that's all I need there right now. I am going to put the methods below.
07:28The reason that I have to declare it up here is I'm going to actually use it here in a
07:32section, one of the properties in here apart from my UI properties here, titleField, authorField
07:37and dateField, and apart from this Course, one is I need something that will actually
07:41hold a reference to whoever is going to be my delegate.
07:44Now this should be the CourseTableViewController, but could be anything, so I'll create a property.
07:52This can just be weak.
07:56So just an ID of something that volunteers to be my delegate, and we'll call it delegate.
08:03Now below this I can finally define what that actual delegate needs to provide.
08:06What I'm really wanting to do is say that this modal view controller can do two things,
08:12it can say hey, somebody clicked my Save button, or hey, somebody clicked my Cancel button,
08:17and it's going to pass off those behaviors to whoever is volunteering to be the delegate.
08:22So let's go back to the protocol. Let's do it below.
08:29So in here I need a list of two methods that the delegate will need to provide.
08:34They both can return void, the first one will be-- let's call it addCourseViewControllerDidSave.
08:39It doesn't matter, you could just say save, or I saved, but usually they're named something
08:45like this, and that will take no arguments.
08:49And the second one will also return void, and we'll call this one AddCourseViewControllerDidCancel.
08:56Somebody clicked the Cancel button.
08:58But seeing as what I'm doing is I am creating a new object and passing it in, well, if somebody
09:03says they want to save, I can just go ahead and save.
09:06They said they want to cancel. I have to make sure that that object is removed, because
09:11otherwise, it will be sitting around in a managed object context, we don't want that, so I'll
09:15have a parameter for this that they can pass back which will be a pointer to a course object.
09:24And this will be the course that needs deleting. And that's my protocol defined.
09:29Now if you don't do much with protocols, this probably looks a little confusing, just a way
09:33we can really pass around behaviors in between this modal view controller and the table view
09:39controller that's going to open it. So how do we go ahead and do that?
09:42Well, I'm going to jump across into that CoursesTableViewController, which is where most of the rest of the work happens.
09:49First, jump over to the header file, because there is a couple of things that don't need to happen here.
09:53One, if this one is going to be tightly wound with that AddCourseViewController, it better
09:59know about it, so we'll import the header file.
10:02Next, we're going to volunteer and say we are able to be a delegate for that course, so
10:08AddCourseViewControllerDelegate in the angle brackets.
10:12We're not done yet, but at least we can support that behavior.
10:15Well, how is this all going to work?
10:17Well, the whole idea is that when I run this application, I am just going ahead and running
10:22it to kind of remind you what we're trying to do here.
10:25When we press this button, we are going to create an object, pass it into that modal
10:30view controller, and tell the modal view controller, hey, I am your delegate.
10:35So that when somebody in here clicks Save or Cancel, we're passing those notifications
10:40back, and we can either Cancel and dismiss and get rid of that object, or we can Save
10:45it to the persistent store.
10:46So I am just going to quit back out of this, going back into CoursesTableViewController,
10:51because the first question I have is well, when do I create that object and pass it in?
10:56Well, it's going to be when the segue happens.
10:59So what I need to do in my implementation is have a prepare for segue method here.
11:04Now in my implementation file I am already being told I've got an incomplete implementation
11:08because I haven't actually provided the implementation for the ViewControllerDidCancel, ViewControllerDidSave.
11:16Now we'll do that in a moment.
11:18What I need first is to provide methods to say when we're about to do a segue, I am going
11:23to create a new course object and pass it in.
11:25And that's a built-in method called prepareForSegue.
11:30So to void method, as I start typing P-R-E-P, it should pop up just click to make those
11:37go away for a moment.
11:38Well, what I first want to do is make sure it's the right segue, we could potentially
11:42have multiple different segues going on.
11:45Am I doing the modal one to add a new course? Am I doing a push one to show the details
11:50of a course? So I better ask if it's the right one.
11:56We'll always have something called segue identifier being passed into this method, so I can ask
12:02is it equal to a particular string? I am going to just call it addCourse.
12:06Now the question being, where does that come from?
12:08Well, I'll show you.
12:10If I jump back over into my Storyboard, this is the object that's my segue here, so when
12:18I'm clicking around I should be able to click on the icon here and select it, and if I do
12:22that and then open up my utilities panel, I should be able to give this Storyboard segue
12:28an identifier if I am in the Attributes Inspector.
12:32So I'm going to call this addCourse, lowercase a, uppercase C, so that back in my code I can ask for it.
12:41So if it's addCourse, I know we're in the right of segue.
12:45So the code I am adding here on line 24, I am going to just create a new pointer to an
12:51AddCourseViewController, I am going to get that from the destination view controller
12:54property of the segue.
12:55We know that we're segueing to the right place, so this gives me a handle on the view controller we're going to.
13:01Then with that, I can reach in and set its delegate to self, meaning me.
13:06We're about to create that new modal view controller, and we're telling it your delegate
13:10is me so look back to me when somebody clicks that Cancel or Save button.
13:15Now on line 127, well, this is where creating the newCourse managed object, we're using
13:21that NSEntityDescription, insertNewObjectForEntityForName in a particular ManagedObjectContext, this
13:28is what we saw when we were creating objects a few sections ago. This is the classic Core Data way to do it.
13:34And with that new object created, we can reach into this modal window we're just about to
13:40segue to and say, hey, here's your current course, it's this object.
13:44If you want to change it, change it. If you don't, you can Cancel back out, and we'll get rid of it.
13:49So I am going to jump back over it into that AddCourseViewController.
13:52This is the one we're modally segueing to.
13:56I want to load some code to load this new object into our interface.
14:00So I am going to jump into viewDidLoad, first I'll set the titleField, grabbing whatever
14:06is in that newCourse object, same with the authorField.
14:10If you remember, I set default values on the entity in uppercase, so we can hopefully see if this works.
14:17However, I can't just do that with the date, because that would be a little long just taking
14:21a regular date string, so let me just do a quick bit of date formatting.
14:30So create a DateFormatter object, set its format to a pretty conventional, say 2012-01-01.
14:41And then convert that from the releaseDate object in that currentCourse object that's being passed in.
14:49Now that's going to work in our viewDidLoad to grab the contents out of the object and
14:54put it in our user interface, but when we actually hit Save, what we're going to want
14:58to do is do it the other way around.
15:02And I'll just copy some of that formatted code here so that we can convert the text
15:13field of the date back into a date object.
15:20So grabbing the contents of the dateField, text field on the user interface, converting
15:24that into a date object and saving that in the currentCourse releaseDate property.
15:30The question is, well, what else happens in the save, are we saving the context yet?
15:34No we're not doing any of that.
15:35There is one more line that I need to add to both the Cancel method and the Save method here.
15:41It's not to actually dismiss this modal view, because we're not supposed to do that.
15:45What we're supposed to do is to announce to the delegate that they are ready to take this for us.
15:51So what I'll do is talk to my own delegate property, and it's going to immediately pop
15:55up my delegate methods here, which are addCourseViewControllerDidCancel and addCourseViewControllerDidSave.
16:01I'm in the Cancel section, which means I do want to pass in the currentCourse object.
16:09And very similar to that, in the Save method we're just telling the delegate, hey, somebody
16:13click this button, addCourseViewControllerDidSave, you go ahead and do whatever you want to do.
16:19Now we need to respond to these delegate methods back over in the CoursesTableViewController.
16:25So I'll jump back over to the CoursesTableViewController file where it's still complaining to us that
16:30we have an incomplete implementation, so let's go ahead and fix that.
16:36Now both of these are void methods, and they both start with addCourseViewControllerDidSave,
16:42addCourseViewControllerDidCancel, there we go, there is the first one, there is the Cancel.
16:46What it's doing is passing back a parameter called courseToDelete.
16:51What I can do is tell the managedObjectContext, hey, we don't need that object anymore, you're
16:56keeping track of it, it's in the scratch but go ahead and get rid of it, we're not going to save it.
17:01I am just going to create a temporary variable to hold onto our context object.
17:05I'll just call it context because then all I can do is call the deleteObject method of that, not
17:13deleted objects, but delete object passing in courseToDelete.
17:17That's it. That's me telling the managedObjectContext, we don't need that object anymore, get rid
17:24of it, we're not going to save it.
17:26The last thing we need to do is dismiss that view controller, which is going to be dismissModalViewControllerAnimated
17:32with a Bool of yes.
17:34Now, we've got one more method to add, again a void method.
17:40This one is the addCourseViewControllerDidSave.
17:47And again, what I am really wanting out of this is to grab hold of the managedObjectContext
17:52and tell it that we're going to save.
17:54First, we're going to create a no pointer to an NSError. I am just going to grab a handle
17:58on the managedObjectContext just to make line 31 be a bit shorter.
18:03And again, we're just calling Save on that context.
18:06It is a negative check, so I've got the exclamation mark because I am only interested if there
18:10was a problem. We're going to pop up the NSLog message, otherwise we're assuming everything
18:14is fine, we'll go ahead and just dismiss that ModalViewController.
18:18I am going to hit Save and run.
18:25Pops up the Table View, nothing there which is what I would expect.
18:27I click the plus button, it looks like we're loading in the default values, which is why
18:32I wrote them in uppercase so that they're hopefully recognizable.
18:35I could do a bit of experimenting with the font size here, but this will work.
18:38If I click in there, say New Title, New Author, I'm going to leave the default value of the
18:46Release Date and hit Save. We jump back, and I don't see anything.
18:51Well, here is the problem. It probably did work, but while this managedObjectContext
18:56is saving--and I am assuming doing that correctly-- we need to have a little bit more communication
19:01going on between the fetchedResultsController and the Table View to make sure this is refreshed.
19:05I am just going to try one more thing before we go on to that, I am going to click the
19:09plus and make sure that we can just cancel out of it.
19:12So at least the delegate information seems to be working correctly.
19:16So in the next section, we're going to see how to do a bit better communication between
19:20the fetchedResultsController and the Table View, so it can refresh itself when it needs to.
Collapse this transcript
Responding to changes in the underlying objects
00:00At the end of the previous example it didn't look like objects were being created, but
00:05in fact they were, and if I quit out of the application and run it again, it will reload
00:10promptly and fetch those objects out of the data store.
00:13So it is actually working, the issue being it wasn't refreshing itself.
00:19So we're going to make that work now, and we're still going to work with that NSFetchedResultsController.
00:24It does make our lives a lot easier when connecting fetch results to a Table View, but like everything
00:29else in iOS, it can exist at several levels of complexity.
00:33And if we want to get more complexity out of it, like everything else in iOS, we're
00:37going to use a touch of delegation.
00:40So what I'm going to do is in this course's Table View Controller where we're already
00:44using the fetched results controller, I'm also going to volunteer to be a delegate for it.
00:49So everything I'm going to add will be in the CoursesTableViewController class file.
00:53First off, I'm going to jump into the header file and volunteer, say that I can be a delegate
01:00for NSFetchedResultsController, and it should show up right there.
01:04I'm already a delegate for that modal window, so I'm putting the comma and then both of
01:09these delegate protocols inside the angle brackets.
01:12But that's just me saying that I can be. It doesn't mean that I am at yet.
01:16So I'm going to jump across into the implementation, and using the jump bar jump down to the Fetched
01:21Results Controller section.
01:24This is where I am instantiating the fetchedResultsController, and this would be the place to tell it, hey, I'm your delegate.
01:32So right before I return it, I'm going to add a new line that points to it, accesses
01:39its delegate property, and says, self, look to me for any of your delegate methods.
01:45So what does that mean? Well, now it's going to call us.
01:49It's actually going to call us several different times in several different circumstances.
01:53We have four delegate methods we're really interested in here that if I add them, we'll
01:58automatically be called in response to any changes in that fetchedResultsController.
02:03The first two are pretty simple.
02:05I start typing void, and then what I'm looking for begins with the word controller.
02:10What we're looking for is the controllerWillChangeContent, and the controllerDidChangeContent.
02:16These are pretty straightforward.
02:17What I'm going to do in here is just tell the Table View that we are about to begin
02:22some updates, and then I'm going to add another method that will be called automatically at
02:30the end of updates to the fetchedResultsController, and we can tell the tableView we're done.
02:35So controllerDidChangeContent. We have finished processing those changes.
02:47So tableView endUpdates. So 50% done in the delegate methods.
02:53We need two more, and this is all because we can make different kinds of changes to
02:58that NSFetchedResultsController.
03:01We might be doing inserts, creating new objects, that's what we've just done here, but we
03:05could also be deleting them or we could be updating them, or in a long list we could
03:10be moving things around if they have to show up in a particular sequence.
03:15So I could just write the code to respond to the inserts that we're doing and refresh
03:19the tableView there, but then we would have to come back several times to make it work
03:23with deletes and changes and so on. So I'm going to fully configure it now.
03:27So the next one that I'm going to create is also void, and it begins with controller,
03:31and it's a much longer one here.
03:33We're looking for the didChangeObject: atIndexPath: forChangeType: newIndexPath.
03:41It gives us multiple options, because this is called multiple times under different circumstances.
03:48The first thing that I want to do is grab hold of the tableView that I'm working with,
03:53and rather than call self.tableView all the time, I'll just create a temporary placeholder for it.
04:04The main thing I'm going to ask in this method is, hey, what kind of change just happened?
04:09Because this will be called automatically when all kinds of changes happen.
04:13There will be passing in an object, hey, this object has changed.
04:16Well, what do you mean by that, was it an insert, was it a delete, was it a change, was it a move?
04:22So we get not only the objects being passed in, but the change type, and I do different
04:27things based on the change type.
04:29So I'm actually going to create a switch statement here.
04:32And we're going to switch on type.
04:34Now, you've seen that I like to always write out code in full.
04:38I'm not a big fan of copying and pasting stuff.
04:41I'm going to take a break from that habit and just paste in a little code here, simply
04:45because it would be really tedious for me to type it all in, certainly tedious for you to watch it.
04:50So let me copy in some code here, and I am just going to explain what this does.
04:55So I'm just pasting in those options inside that switch type, because we're breaking down into four things.
05:03If there's an insert, so we have this enumeration we're checking, what type of change was it? It's an insert.
05:08Okay, well, tell the tableView to insert a particular row at this newIndexPath with a fade.
05:15Okay, done. Very similar to that, it's a delete. Okay, if it's a delete, what do we do?
05:20We tell the tableView to update itself but to delete a particular row.
05:24If it's a change, we need to change the contents of a particular cell, so what I'm doing here
05:30is I'm first talking to the fetchedResultsController and saying, hey, give me the course at your particular index.
05:37Then what I can do is grab the current cell at the current location in the tableView and change its title.
05:44Now, in case you don't do a lot of this, because I'm creating new objects here, I have to wrap
05:48this up in curly braces within a case statement just to make it work.
05:53Finally, we have the idea that something could move.
05:57This really wouldn't be a problem for our example, but if you had a lot of rows being
06:02brought back with perhaps a sequence number, you might change them to change the order around there.
06:08So effectively what this is doing is both a delete the original position and insert
06:12at the new position.
06:14And that's that method taken care of. We're updating the individual objects.
06:18We have one more delegate method for fetchedResultsController.
06:21Again, void, and again beginning with controller, and it's this one, didChangeSection this time.
06:29Not the individual object, but we changed a section, and there is a particular kind of change.
06:34Really, what we are interested in, too, did we delete a section or did we add a section?
06:39Because what might be happening here is if we're inserting a new row into an existing
06:44section, then this doesn't call, but because we're automatically splitting sections up
06:49by author, that might mean that just by creating new course object with a author, we need a new section.
06:56Or if we delete the last course for a particular author, we need to delete a section.
07:01Now, this method will be called automatically at the right time. It just needs to know what
07:05it's supposed to do.
07:08Once more, what's happening is when this is called, we will have a fetchedResultsChangeType called type.
07:13So I'm going to switch on that again, but it's actually simpler here, I only need to
07:17switch on two options.
07:20But once again, I'm going to paste it in just to save some time.
07:23These are my two options. Was there an insert?
07:26If there was an insert, tell the tableView to insert a new section at a particular index and fade that in.
07:34If not, there was a delete, in which case tell the tableView to delete a section.
07:38There is a bunch of code here, none of which is particularly complex, most of which you're
07:43going to start kind of putting in your own personal library, because it's the style of
07:47code you would use all the time with Core Data.
07:49I'm going to save this, build it, see if it's giving me any issues.
07:52It doesn't look like it, and go ahead and run the application.
07:57So I've got three so far.
07:58We're going to see in a moment how to delete these old ones.
08:01I'm going to add a New Course, click into Title, let's call it Core Data, Author is
08:09Simon, I'll leave the date as is or at least change it a little bit. Why not? 10-16.
08:15This has to follow the format year-month-day, and it should work fine. We'll click Save.
08:22And there we go, we're updating, we're adding a new section automatically, because it didn't
08:27have an author with that name.
08:28To double-check that, that works correctly, if I create a New Course here, let's call
08:33this Objective-C and use the same Author name Simon, we shouldn't get a new section, it
08:38should appear under the same section, and indeed it does.
08:42However, we've got some messy stuff here.
08:44It would be nice if I could start to tidy this up.
08:46And in the next section, we'll see how do we actually start deleting, because that's a
08:50lot easier than anything we've done so far.
Collapse this transcript
Deleting objects from the table view
00:00So we just added a whole bunch of code to the Courses Table View Controller to deal
00:05with updating the table view in the case of changes and updates and deletes, so let's
00:10have the Delete feature.
00:12Now this one is much simpler, and it won't require a new view controller, and we actually
00:16already have all the delegate method in place to respond to deletions, so the table view
00:20should be updated automatically.
00:22So everything I need to do is in the CoursesTableViewController.m. What I've got to do is scan through a little
00:29bit because these days by default they're going to provide a method that's commented
00:34out, and this is the one that I want.
00:37I can't use the jump bar for this because it is commented out, but it's commented out
00:41right now with Override to support editing the table view.
00:44So I'm going to remove those comments here.
00:47This is what we're looking for, the tableView: commitEditingStyle: forRowAtIndexPath.
00:54I am actually going to delete most of it because I don't need the second else, and I actually
01:00don't need the thing that's trying to change the table view, that's already going to be
01:04taking care of by all those delegate methods that we put in.
01:08So this is what I start with, if (editingStyle == UITableViewCellEditingStyleDelete),
01:15did they delete something?
01:16Well, if so, what I've got to do is I got to grab the managedObjectContext, and I've
01:21got to grab the actual course that somebody is just trying to delete and then tell the
01:25context, hey, delete this guy.
01:27So what I could combine this all into one or two lines, let's just break it apart for clarity.
01:35So here, the first three lines, 137, grab the managedObjectContext, and 138, ask the fetchedResultsController,
01:43for whatever object is it the path being passed in, which is passed in to this method,
01:48we have that parameter.
01:50That will give me the course we're trying to delete, and then all I do is on the next
01:54line just pass that object to context, say, hey, go and delete this course.
01:59Well, if I'm deleting that object, that will not save those changes. It will not persist
02:05them, so I'm going to go ahead and tell the context to then save.
02:09We save the user way first by creating an error pointer, then we call the save method
02:18of the managedObjectContext, which I had already grabbed as a variable called Context.
02:24And again, we're negating that check because if it works, it returns yes, and we were only
02:28interested if it returns no. We'll pop up an NSLog, but we are assuming and hoping it will work.
02:34Let's check it out. Save that and run it.
02:39So I have the few courses that I've added. Let me get rid of the second one. Swipe Delete.
02:44Not only do we delete we delete and update as well.
02:48Swipe and Delete, swipe and delete. And those changes should be persistent.
02:54If I quit out of this and go back in and run it again, all I'd expect is those two entries, looks good.
03:01And that's adding delete to the view controller.
Collapse this transcript
Creating a display view controller
00:00Next up, we're going to add another View Controller to the storyboard.
00:04This one will be used for both displaying the details of a particular course and also
00:09for editing an existing one.
00:11We don't have to do it that way, but we're going to make this exist in two different modes.
00:16And this will actually be a lot easier than the add screen because we're not going to
00:20mess with modal delegation.
00:22So I'm just going to zoom out for a moment, so I get a better view here.
00:26Open up my Utilities Panel and from the Objects library, drag on a regular View Controller.
00:32It doesn't really matter where I put it. I'll just drag this to the side a little bit and zoom back in.
00:43Now what I want to have happen is that when we just tap a normal cell, that we're going
00:50to go to the detail view. That's the standard iOS way of doing things, select it, move to the detail view.
00:56So that's all we need to do is create a segue between the cell and this view controller.
01:00Now be careful here, because I'm not going to Ctrl-drag from the Table View or from the button,
01:05I want to make sure the cell is highlighted, then hold Ctrl down, click and drag over,
01:11and we're going to do a push segue, which should mean that will automatically be embedded inside
01:17the navigation controller, and when I select it we should see the Blue bar up here at the top.
01:22Now because naming your segues is a good idea, we've got multiple segues out of that first
01:28table view, I'm going to select it, open up the Utilities Panel, and give it be Identifier of showDetail.
01:37Now we will need a custom view controller class for this. It's not going to be a lot
01:41of code, but we'll need one anyway.
01:43So I'll add a New > File, it's in Objective-C class.
01:49Because this will work as both the display and edit, I will call it DisplayEditViewController.
01:54Again, I want to make sure it's a subclass of UIViewController, not UITableViewController
02:01or anything else, and click Next.
02:04Create it and then just make sure that in the storyboard that View Controller is actually
02:11hooked up, and I think its identity is the custom class, not the generic UIViewController.
02:16There we go, DisplayEditViewController.
02:20Same way that I did before, what I want to be able to do is pass in a reference to the current course objects.
02:25So I'll do that first, jump into the header file for this new DisplayEditViewController and add a property.
02:38It's a little puzzled because I don't have an import to the Course.header file, so we'll do that now.
02:44And that should get rid of my error message.
02:49Don't need to synthesize it. The only reason I would want to do if I wanted to avoid using
02:53the Underscore for the internal instance variable.
02:57So jump back into the Storyboard, I need to add some features to this View Controller,
03:01it's a little bit plain for right now.
03:02I could create them in code, but it's easier here.
03:05I'm going to define three text fields and drag them all.
03:08Now I'm going to make this exist in two different states.
03:11So the first time around while I'm going to drag these text fields wider, I'm going to
03:15change them so they're not enabled.
03:19They are a little close to the top for me right now, so I'm going to drag them down a bit.
03:25With them all selected, I'm going to jump into the Attributes Inspector, change their Border
03:30Style to None, and also come down and find Enabled and turn that off, because the first
03:37view of this is just viewing them not editing them.
03:43I'm now going to switch into Assistant mode, selecting that display view controller should
03:50bring us up the DisplayEditViewController.h file which is where I want them all, and we'll
03:54just grab them one at a time, Ctrl-drag and create an Outlet, for titleField, authorField and dateField.
04:06Let me switch back to standard editor, and I'm going to jump into the class file for that.
04:14So into the implementation here, and I'm going into viewDidLoad.
04:18Well, I'm going to grab the contents of the course objects that were passed in and just
04:23set those to the values of my text fields.
04:31So that should take care of populating those fields when this View Controller appears.
04:42But to make sure we actually have a course object given to this detail view, I need to
04:47make sure I'm passing it from our main Table View Controller when we do the segue to this.
04:52So I'm going to jump over into CoursesTableViewController and find the prepareForSegue, which right now
05:00is just segueing to the other view controller, that's the addCourse one.
05:04So I'm going to add a new if statement, and my new segue was called showDetail.
05:13And now I just need to add two or three lines here to make sure I'm passing in a new object.
05:19First, what I need to do is grab a reference to the View Controller I'm segueing to, and
05:23this class doesn't know about it right now, so let me zoom back up to the top and do an
05:28import for it, DisplayEditViewController, jump back into prepareForSegue, and what we
05:37need to do is create an instance of that.
05:46Very similar to the first line I did in the previous segue by getting it from the destinationViewController
05:52property, and now we need to find the correct object to pass that in.
05:57I'm going to ask that from the table view first by grabbing an indexPath and just need
06:02indexPathForSelectedRow, because that will give me the row that someone has just tapped
06:07on, and use that to go against the fetchedResultsController, find the course object that's at that IndexPath,
06:19and save it just in a temporary reference called selectedCourse.
06:24Finally, I can use that to just set that currentCourse property in the View Controller we're about to segue to.
06:33Save that, run it.
06:40So we should be able to click on one of these and jump into the details for that course,
06:46the title, the author, the release date.
06:48We still have our separate segue from the bottom up here. Let me Cancel out of that,
06:53but we can jump in and see the details of a particular course.
06:56It might not be the prettiest interface in the world, but it works.
07:00Next, we'll hit the last big part, changing this detail view controller so that it also
07:05works as an edit screen.
Collapse this transcript
Switching modes in a view controller
00:00So we currently have this DetailViewController just showing up un-clickable, just displaying the data.
00:06I'm also going to make this work in Edit mode so it allows us to edit the item that we are currently looking at.
00:12So jumping back onto the Storyboard, I am going to work with this DisplayEditViewController again,
00:20open up my Utilities panel, and I want a couple of buttons I am going to drag on. I can just
00:25remind myself where these text fields are because I currently have them without a boundary.
00:29I am going to drag on a couple of buttons just up at the top here, and just so they don't
00:36interfere a little bit, I will make them a little bit smaller.
00:45One says edit, the other says done, I am actually going to change the done button so that it is hidden
00:52and then shift into Assistant View so we should be looking at the DisplayEditViewController
00:56on the right-hand side, and I'm going to drag from both of these buttons and create actions for both of them.
01:02Although I also want outlets as well, so let's do both of them.
01:06So Ctrl-drag from Edit, I will set first an outlet, call it editButton, and then I will
01:13Ctrl-drag, and this time do an action. And I'll call the Action startEditing.
01:23And then from the done button, Ctrl-drag, first creating an outlet, doesn't really matter
01:28where I put it, and then Ctrl-drag and create an action, doneEditing, and let me just tidy
01:39that up a little bit and put my IBActions together.
01:44Now I am sure many of you have already figured out exactly what I'm doing here, and yes of
01:49course, I could make them the same button. I could just toggle the state of the button.
01:54I'll toggle the words on the button. This would just be a very simple way of doing this.
01:58We are going to shift into Editing mode, let me click edit, and that button will disappear,
02:02and the doneButton will appear and then when we're finished editing, we click done, and
02:05we shift back into the normal display mode.
02:08So get out of Assistant mode and into the DisplayEditViewController implementation file,
02:14where I should have the startEditing and doneEditing entries.
02:18Now as you know, I don't tend to paste a lot of code, the only code I will occasionally
02:22paste from time to time is where it's entirely uninteresting, and that's what this code is.
02:27I am just going to paste in some.
02:29And all we're doing here first is turning on titleField, authorField, and dateField, because
02:35I'd disabled them in the Basic Display mode, and then changing their borderStyle to actually
02:40have the classic rounded rect to be another visual affordance so that they are editable,
02:45I'll then hide the editButton, and I'll turn on the doneButton, that's that one done.
02:50And then in the doneEditing, I have the flipside of this code that I am just going to paste
02:55in here where when we're finished editing, we will set those again to disabled or rather
03:00enabled = NO, turn their borderStyle back Off, hide the doneButton, and make the editButton visible.
03:07Now we got one more block of code here which is if we have done some edits on those text
03:12fields, I want to take those values and put them back in that object that we currently
03:16have that we are holding in memory of the current course.
03:19Because without actually making those updates, there is going to be nothing for the context to save.
03:23So again, that's the very uninteresting code. There are two lines that are interesting,
03:28which is if we are done editing, I need to be able to tell the managedObjectContext to save.
03:33Here is a problem, though, this current DisplayEditViewController, if I jump across to say the header file for
03:40that, I actually don't have a reference to the managedObjectContext. I can't call save on that.
03:46Well, what I could do is create a property for that and pass it in, but let me show you
03:51another way of doing it.
03:52Of course, I know that in my AppDelegate that managedObjectContext is declared as a property,
03:59I should be able to get to it.
04:01It's a little tedious to go this way if I wanted to do it multiple times, but if I just
04:05want to do it once, here's how I'd grab hold of it.
04:08Jump back into my implementation and after this line which is where I want to grab hold
04:13of the managedObjectContext, what I'll do is I will create a reference to AppDelegate.
04:17At the moment it doesn't know what that is, so I am going to go ahead and just call Import
04:22on it, so it knows about the existence of my custom AppDelegate class, import AppDelegate.h.
04:29And now we should be able to create a reference to the AppDelegate class. I will just call
04:33it myApp and grab hold of that by casting to an AppDelegate from a call to UIApplication,
04:43sharedApplication, although be careful here because you don't just want the application
04:48object, you want the Application object's Delegate. That's what you are is the AppDelegate.
04:54That now gives me a handle to the running AppDelegate, and it allows me to grab hold
04:59of say the managedObjectContext.
05:01However, I don't even have to do that because then I'd create an NSError object and call
05:06it, but just jumping back to my AppDelegate, we haven't really used this much, but AppDelegate
05:12itself has a method called saveContext, which just goes ahead and does that.
05:19So why not just call that directly?
05:21Jump back into my DisplayEditViewController, and now I've got a handle to AppDelegate,
05:25I just say myApp saveContext. Save this, build it, no issues, let's run it.
05:35So I should be able to click on one of these courses, shift into Edit mode, change the
05:40name, done, and if I click back on Courses, I should see that new section being automatically added.
05:50Click this other one, edit, done, and back and they should be combined into the same section.
05:59And we have that view controller now existing in two different modes.
06:05There was one more thing I wanted to do, which is kind of tangential, but it's quick, so bear with me here.
06:12Currently, when I click the plus button we're getting this New Course appear with our default
06:16values of TITLE in uppercase, AUTHOR in uppercase, and a fixed Release Date of 2012-01-01.
06:23And that's because canceling out of this and going back into the project, in my data model
06:28what I have going on here is Author has its default value of AUTHOR, title has its default
06:34value, and releaseDate has this set string here.
06:38It's not very useful, because often what you want with a date is you want something that
06:42moves, and putting in 2012-01-01 is not going to be all that useful in a month or a year or whatever.
06:49So let's say we want this to default to today's date.
06:52Well, how do we do that?
06:53Well, there are a couple of shortcuts for entries you can put in here, but none of them are
06:57going to have the effect that we want.
06:59What I'd actually like to have is I am going to clear that one out, and I'm going to do
07:03this in code, I am going to have custom behavior in our course class to provide a default value.
07:08So the idea is when this course is actually instantiated, we will set that value in code
07:15if it wasn't capable of doing it in the actual data modeler.
07:17Now the question is, well, where? Do we just do it in the init method?
07:22And no, you don't, because init is not the best place to put it, because it's not you
07:25who is initializing. So here's what I want it to say.
07:28In a custom class that you have, if you want to add a little bit of custom behavior to
07:32deal with initialization, and this of course means that we are inheriting from NSManagedObject,
07:36what I am going to do in here is create a void method for awakeFromInsert.
07:44This is the method that's going to be called when a managed object is instantiated, and
07:49when you want to inject your code into that process of waking this up after it's being
07:54inserted for the first time.
07:56Usually what we do is first a super call and then any custom code that we have, which in
08:01our case is simply self.releaseDate=, and let's do it to the current date.
08:09That's it, custom behavior that will now be joining in the whole Core Data process.
08:14So our data model no longer has a default value for date, but when I run this application,
08:21and when we decide to create a new course, it should be defaulting to today, and I should
08:26see that value, there we go, ninth of September, 2012, and there we go.
08:33Yes of course, this app could do with some finishing touches, particularly with things
08:37like the dates and having date pickers, but we have everything going on, we have fetched
08:41results controllers, we have table views grouped into sections, we have modal segues, we have
08:47pushed segues, we have Core Data modeling, Core Data fetching, Core Data creation, editing,
08:53deleting and all of it from a completely empty project.
08:57So if you made it all the way through, nice job.
Collapse this transcript
6. Putting It Together: Cocoa
Creating a Core Data Cocoa app without code
00:00In the next few minutes we are going to take all the same Core Data ideas, entities, managed
00:05object context, saving, fetching, deleting and put them all together again, but this
00:09time in a Cocoa desktop style app.
00:12And seeing as we did rather get buried in code doing this in iOS, this time around we
00:17are going to take a different approach.
00:19I am going to create a new OS X application, this is a Cocoa Application, and I'll call
00:25this CodelessDemo, because we are going to do this without writing any Objective-C.
00:30This would not be possible in iOS, but we can do it in Cocoa because of Cocoa bindings.
00:36So I'll give that a name, I want to make sure that Document-Based Application is unchecked,
00:40Core Data and ARC are both checked, and everything else is unchecked.
00:45Now of course, in a typical Cocoa App you will be writing code, but putting this constraint
00:49on ourselves, seeing how much can be done without writing Objective-C will give you
00:54a good idea of the power of Cocoa bindings when used together with Core Data, and I am
00:59assuming that you know at least the basics of Cocoa bindings.
01:02If you're coming from a pure iOS background where they aren't available, I'd recommend
01:06you take a look at the relevant sections in the Cocoa Essential Training course first.
01:11So we've got this brand-new application, there is nothing in my data model, there is nothing
01:16on the user interface, I'm going to create another course entity here with three attributes, title and author that
01:28are both strings and releaseDate, that's a date. I'll create some basic default values for them.
01:39And once I make sure that they are defined and named, I am just going to go up to the
01:43Editor menu and create the subclass for these and add that to the project.
01:50I am still not writing any code. Obviously, we do have Objective-C, I am just not writing any myself.
01:56Once that's done, I am going to jump over to the main Window in our MainMenu.xib file,
02:02so clicking the Window to open that up.
02:04Now I am going to go over to my Object Library in my Utilities panel, and from the Data Views
02:10section drag on a Table View.
02:17Drag it a little bit wider here. I'm leaving a little room at the bottom because I want
02:22the ability to add a couple of buttons.
02:25Now if I don't do that now, just jumping to my controls section, I'll drag on a Gradient
02:30button, I actually want this to just have the plus sign to allow us to add a new entry.
02:37So in my Attributes Inspector, I am going to get rid of the title, and then from the
02:41Image section what I am looking for is the NSAddTemplate, and that will change this to
02:46the plus sign down here that looks good, doesn't need to be that wide, so I'll drag it a bit
02:51closer and then just do an Option-drag to copy that.
02:55So I have another button, but this time I'm going to change that to NSRemoveTemplate,
03:01so we have the plus and the minus signs here.
03:04Everything's standard Cocoa element so far, but now what I need is a controller object
03:09to handle the communication between this Table View and our collection of course entities.
03:16So what I am going to do is go into my Object Library again, select from the Objects & Controllers
03:21section, what I am looking for is this guy, Array Controller.
03:25This is the same controller I'd use for doing binding in non-Core Data applications, so
03:30if I just wanted to connect a Table View to an array or a mutable array, but it is also Core Data aware.
03:39It's a non-visual object, so I drag it over here into the dock, it doesn't actually have
03:43any kind of appearance on my window. And now I need to configure that.
03:48I'm going to tell it where it's supposed to get its data from.
03:52And what I can do is point it directly the managed object context.
03:56So with this Array Controller selected I'm going to come up here, and what I'm looking
04:00for is my Bindings Inspector which is the last, but one and usually I'd connect this
04:06here to say a Content Array.
04:09What I am looking for is the Managed Object Context section down here, I am going to expand that.
04:14And Managed Object Context is an accessible property of my AppDelegate so I can connect
04:19it right there and have this guy act as the middleman between the data and my user interface.
04:26So select to Bind to, from the dropdown, select App Delegate, and what I'm looking for is the
04:33property called Managed Object Context which will be part of self, so self.
04:37It should appear as I start to type--yes, there we go, managedObjectModel and managedObjectContext,
04:43that's the one that I want.
04:44However, this is the secret to using this with Core Data, not just connecting to the
04:49Managed Object Context because we could potentially have lots of different entities here.
04:54So what I then do is go over to this section of the Inspector, the Attributes Inspector,
05:00and right now where it's saying, well, I'm expecting to represent a class of type NSMutableDictionary,
05:06I'm going to select that dropdown and say, no, you are going to go with entity name,
05:11Course, so not a class, but an entity.
05:15Then we have a couple of checkboxes down here, one is Prepares Content, not the most obvious
05:20one in the world, but for us this basically means as soon as you load, do a fetch of all of these entities.
05:27So that will work. I'll check that. So now we have this Array Controller configured.
05:32It's going to talk to the Managed Object Context and grab all the course entities as soon as
05:37this application loads.
05:39But there's nothing connecting it yet to the user interface.
05:43So I am going to do that now.
05:44Well, first I might want to rearrange this Table View a little bit, give myself a bit
05:49more room to play with, because it's often useful to have the expanded view of the dock
05:54here, so I can select the Table View because it's embedded inside of scroll view.
05:58So I am going to select Table View and change its Columns to 3, drag it a little wider so
06:04I can grab those, I am going to have the first one being Title, so I'll drag him wider, and
06:11the last one is going to end up being the Date. So we have got three columns here, that's fine.
06:16I am going to select the first column and in my Attributes Inspector, give it a Table
06:22Column heading of Title, we should see that appearing up at the top, select the second
06:27column, usually easier to do over on this left-hand side in the dock rather than clicking
06:32around in the actual interface, but that just makes our columns a bit more presentable.
06:41What I need to do is select the Table View itself, you might need to expand it, we are
06:45looking for the Table View inside the Scroll View and with that Table View selected, I
06:50go over into the Bindings Inspector, and I'm going to tell it where it gets its Table Content from.
06:58So down to the Table Content section, open that up and what I want to do is select the
07:02check box and bind that to the Array Controller.
07:06I don't need to say anything else about it, I don't need to say what part of the Array
07:10Controller, I'm basically just saying bind to everything, all the arranged objects.
07:15What I do need to do is then go ahead and say, well, which column is meant to represent
07:19which attribute of those entities.
07:22So then I go through the columns again, selecting the Table Column for Title first and what
07:27I want in here is in the Bindings Inspector again, we open up Value, we are binding to
07:31Array Controller, but this time what I'm interested in is the actual Key Path, what is the attribute
07:37I want in this column.
07:39Well, as I start typing, I should have the pop-up, and this will only happen if I have
07:44created the custom class, but I can see NSString Title, select that, save, jump over to the
07:50next column, bind that to the Array Controller, author, again it should be appearing, jump
07:58over to the next column, select the check box, bind that to the Array Controller, and
08:02this should be release date.
08:05Now if I just go ahead and run this right now, we shouldn't get any problems.
08:08Again, we still haven't written any code, but of course, it isn't very interesting because
08:13we have no data to actually work with.
08:15So what I want to do is have these buttons down here, so when I press the plus button,
08:20create a new course and allow me to configure it.
08:22So, quit out of that, and we need to hook this button up.
08:25Now, usually you'd think about creating an IBAction and writing some code. We don't need to do that.
08:31What I can do is Ctrl-drag from the plus button, over to the Array Controller, let go, and from
08:39the pop-up select add.
08:43Then from the second button, I'll hold down the Ctrl key, drag over to Array Controller,
08:48come down, and select Remove.
08:49Save this, run it, click the plus button, and I can immediately see what I'm getting
08:56is my default values. I could click that several times.
09:01This allows editing by default.
09:03Now it looks like what it's bringing in here for the release date is a full NSDate time string.
09:09That's not what we want, so we'll tidy that up in just a second.
09:13Question is, is it's saving this? Well, let's find out.
09:16If I quit out of this application and then run it again, looks like everything is being loaded.
09:22But quitting back, let's tidy up that release date. What I am going to do over here is click
09:27on the actual text cell, I am going to kind of drill down into that point, and then from
09:32my Objects section here--actually I'll drop that down--I want my Controls.
09:36I am going to filter on Date because what I'm interested in is a Date Formatter, I am
09:42just going to drag that on to the text cell and let go, and that should format it both
09:48on the way out and on the way in, run it again, looks correct.
09:55It will allow us to change the date, create new entries, rearrange the columns, whatever you want.
10:06The Delete key also works.
10:08If I was going to delete a lot, that's a little inconvenient, so one last thing that I might
10:13do is jump back into the interface, select the Delete key here, and then find my Attributes
10:19Inspector, come down to Key Equivalent, click into that and hit the Delete key, so now that
10:28when we hit the Delete key itself, that should allow us to delete them when the application is running.
10:33Let's just double check that, add a bunch of them, select one, hit Delete and clear them out.
10:39We haven't had to write a single line of code, we've got our entities modeled, we are adding,
10:44we are editing, we are deleting, and it's all done very, very simply and very, very
10:49quickly, and even if your main focus is iOS, one thing you may want to use this for is
10:54for creating simple apps that you could use to do a bunch of easy preloading and pre-creation
10:59of objects into a store file that you could then take that store file and include with
11:03a Core Data iOS application. And we'll see how to do that a little later.
11:07But so far, of course, we are still working with individual entities, and even though we
11:11saw how to model relationships earlier on, we haven't really done anything with them.
11:16So next, let's take our data model and make it a bit more interesting.
Collapse this transcript
Creating a drilldown application using multiple entities
00:00Now let's start working with related data both To-One and To-Many Relationships.
00:05I've got a simple project here, and what I've done in the data model is I've created this
00:11Course entity that now just has title and releaseDate, and I've split Author out into its own entity.
00:17It would be fine if all I was interested in was the author name for each course, but if
00:22I also want the phone and the email and the bio, I don't want to have to repeat that information
00:27along with the course every single time. So I have got these two entities.
00:31I haven't created a relationship between the two, so let's do that now.
00:35First, I'm going to create the simple relationship between Course to Author, so I'll switch back
00:41into the normal table style, make sure that the Course entity is selected, click the plus
00:45button, and I am going to call this author, we are going from Course to Author, each Course has one Author.
00:53Now the other way around going from Author to Course, click the plus button, this time
00:59one author could have multiple courses.
01:01So I am going to call this courses in plural, and that will be the name of our property,
01:06Destination is Course and seeing as we now have both of them, I am going to select this
01:11as the Inverse relationship.
01:14However, while this is selected, what I do want to do is come over into my Utilities
01:18panel and make sure this is checked as the To-Many Relationship, that will be in your
01:24Data Model Inspector section.
01:26And that should do it, flipping over into the Graph View, we can see that we have these
01:31represented here. Author to Course is a To-Many Relationship, Course to Author is a To-One Relationship.
01:38And then I am also going to go up to the Editor and burn off the subclasses for these.
01:44I only had Author selected there. I need to make sure both Author and Course are created,
01:51and that will do, so we have got both Course and Author turning up.
01:55Now let me jump over to the user interface.
01:58I've done a little bit of work here just to set it out, but nothing is hooked up yet.
02:02I have got a Table View with one column here, a Table View with two columns here, and two
02:07sets of buttons, but nothing is actually connected anywhere. This is all I've done so far.
02:12But what I want to do is have this left-hand table view show a list of Authors, and this
02:18right-hand table view shows a list all related Courses for any selected Author.
02:24So what I am going to need is two Array Controllers. We are going to need those objects to act
02:28as the middleman between our user interface elements and our data.
02:32So from the Object Library, I'll select Objects & Controllers and drag on two Array Controllers into the dock.
02:40Now one issue is that right now these will both be called Array Controller, and I need
02:44to make sure that I'm pointing to the right one when I start to bind everything.
02:48So I am going to select the first one, we haven't configured it yet.
02:52But go up here into my Identity Inspector, and what I can do down here is give it a Label.
02:58So we are in the third part of the Inspector section, and I'll call this first one AuthorsArrayController.
03:09Select the second one, and we'll call that CoursesArrayController, and this will just
03:14make them more recognizable when we are binding.
03:17Even though you won't actually see them change over here on the dock, they will both have that label.
03:22So I am going to configure the first one, the AuthorsArrayController first.
03:27Very similar to last time, I am going to go up to the Bindings Inspector, find the last
03:31entry here of the Managed Object Context, and say yes, I would like to bind this to the
03:36App Delegate, self.managedObjectContext, because I just want this one to go and fetch all the author entities.
03:47So after the Managed Object Context is selected, I jump over here into my Identity Inspector,
03:53and change this from Class to Entity Name, the Entity Name is Author, singular, and I
03:59want to select the box that says Prepares Content, which means go and fetch all the authors
04:04when this initially loads. That's my Array Controller configured.
04:09It's going to grab that data. I need to do it from the other end, too, which is I need
04:12to connect my Table View to the Array Controller.
04:16So preferring the expanded view of the dock here, I am going to select the Table View,
04:20make sure the Table View itself is selected, not the Scroll View,
04:24come up to the Binding section, and find the Content area, which says here we are binding
04:29to that Array Controller, that's all I need to do for this one, but I then need to get
04:33into that first column, the only column inside here, so selecting the column and then tell
04:39it that its content is coming from AuthorsArrayController with a Key Path of name, and it should be
04:47popping up as I type in N. That's that first part configured.
04:53Next, I have to get onto the CoursesArrayController and binding that to the Courses Table View.
04:59So I'll select that second Array Controller, jump into its Bindings, we are going to bind
05:03this again to App Delegate, self.managedObjectContext is the Model Key Path.
05:10We want this to be loaded with Course Entity, so I am going to jump over to the Attributes
05:14Inspector and change that over from a Class to an Entity Name of Course.
05:21I actually don't need to check the Prepares Content thing here because I need to pause
05:25for a second, think about what do I want this to contain?
05:28See, this is a little different.
05:29I don't want all courses. I'm only interested in the courses for a particular author.
05:36I want this to be a proper drill down ability. And this is how we do it.
05:42I need to add one more binding.
05:43So I am making sure that that CoursesArrayController is selected, I jump to my Binding section
05:49and what we are looking for--it's above the parameters--we are still binding to the Managed
05:53Object Context, but I'm interested in this part that says Content Set, it's a subset of Content.
05:59We are basically interested in the Courses property of the Author.
06:04So what I'm going to do is select to bind to the AuthorsArrayController, and what I'm
06:10interested in is the current selection, but not every part of it. Remember, an author will
06:15have a name and a bio and a date of birth and a phone number but will also have a Courses
06:22property, that's what we named the relationship that will return an array. Well, technically,
06:27it will return a set of course objects.
06:30So I am fueling this now with that set of course objects.
06:34So again, that's the Array Controller configured, but we don't have the Table View configured yet.
06:39The Array Controller is getting the data, but there's nothing actually connecting it
06:43to the user interface.
06:44So, I'll select the Table View, give myself a bit more room here, and with the expanded
06:49view here, I'm selecting the actual Table View itself, say that it's being fueled from that
06:54Array Controller, so Table Content, select Bind to > CoursesArrayController, Done.
06:59Now, I need to go through the two columns.
07:02I could do more. I just have two for the purposes of speed here.
07:06So expand that, select the first column that should represent Title, and I want to select
07:11its Value, check, we are binding to CoursesArrayController, Model Key Path should just be Title.
07:20Jump to the second column, bind that to CoursesArrayController, again, this should only now contain a subset
07:26of relevant courses for any selected author, and its Model Key Path should be release date.
07:33And while I am at it here, I'm going to click into that to find the individual text cell,
07:37and because I know this is a date, I am going to drag on a Date Formatter. You don't have
07:43to do this, but I might as well while I am here.
07:46And we want that in the actual cell. I should be able to see it over here as underneath
07:50the text field cell part of this interface.
07:53Now the next thing I am going to do is none of these buttons actually do anything, but
07:57we saw last time that I can just hook these up to the relevant array controllers, so just
08:01shrinking that down so I can see it a bit better, there is our AuthorsArrayController,
08:05there is our CoursesArrayController.
08:07What I can do from the Author section, Ctrl-drag from the plus button and select add, Ctrl-drag
08:13from the minus button and select remove, and the same to the CoursesArrayController, Ctrl-drag,
08:20add from the plus button and Ctrl-drag, remove from the minus.
08:26So I am going to save that and run it.
08:31Nothing very exciting yet because we have no data, so first I'll add an author.
08:35Click the plus button. It looks like something's being added.
08:39So with an author selected, can we create a course?
08:42It looks like we can, Course Title, Course Title, Course Title. It's editable.
08:51I don't have any default value for Release Date, but I should be able to type something
08:54in here and have that accepted.
09:01Looks pretty good so far, but let me create another author.
09:05That looks all right, but just quite quickly what I'm going to do here, select that and
09:09create a new course, and then I want to select back on David, and see his.
09:18But it doesn't seem to be updating.
09:21So while it allowed us to add in new courses, it does not seem to be refreshing with the
09:26list of David's three courses the way that I would expect.
09:29Now that's because I need one more thing.
09:32And what I am going to do next is kind of the secret sauce of creating a drill down using Cocoa Bindings.
09:37This is something many people miss or don't think can be done without writing a bunch of code.
09:42Here's the problem.
09:43What's happening is that we are selecting in this left-hand Table View, but that's not
09:49officially changing the selected item in the underlying Array Controller, and it's the
09:54underlying Array Controller that's filtering and controlling what's being shown on the right-hand side.
09:59So it doesn't care what we are selecting over here, it's not actually updating itself.
10:04So what I need to do is go back into the interface here, select the left-hand Table View, and do one more thing.
10:13So with that Table View selected, make sure I have got it here, I am going to go over
10:17to my Bindings Inspector.
10:19Now we are already binding this Table View to the AuthorsArrayController, we have got
10:23it connected to the Managed Object Context, but there is one more property I'm interested in,
10:26which is this one: Selection indexes.
10:31And what we are trying to do is map whatever I've selected in the Table View to make sure
10:36that's also considered what is selected in the underlying AuthorsArrayController.
10:41So we click that, and the Controller Key I'm interested in here is not arranged objects,
10:46it's called Selection indexes. It should pop up when I do that.
10:50Save that, and that should work.
10:52If I run this again, we select Simon, I am seeing Simon's first course, we select David,
10:58we are seeing those three courses.
10:59I can create a new author, it should correctly show no courses for that author, but I can
11:07start to add a few.
11:16Now we have got this drill down happening, we are following that relationship, and we
11:20haven't written a single line of Objective-C in this project.
11:24But there is a couple of other things we could start to tidy up here.
11:27What happens, for example, to a course when we delete an author?
11:32I can do that, but now I have no access to those three courses that I had added a little earlier.
11:38So what's actually happening there?
11:39What happens if, for example, we type in some kind of value in Release Date, and we do it
11:45in somewhere that's not actually expected? Please provide a valid value.
11:49Okay, is there any way of prompting what that should be?
11:51So there are a few things we could do to tidy this up.
11:55So next, we'll start adding some code to kind of flesh this out and start getting into working
12:00with some validation and some delete rules.
Collapse this transcript
Responding to validation issues
00:00Let's get a little more involved with validating our data.
00:03I'm going to jump into the data model file here.
00:06Now, we've seen already that when we're working with entities, and the attributes of those
00:10entities, that we can do things like provide a default value for them.
00:14If we're working with numbers, we can provide a minimum and maximum value.
00:17If we're working with strings, we can have minimum and maximum length, and even provide
00:22a regular expression.
00:23So, for example, if I wanted to say that all new authors should have a name that was at
00:28least three characters, I'd select that attribute, and say this had to be a minimum length of
00:333, maybe I wanted a maximum length of 100. That will do.
00:38And save that data model file.
00:40The question is when are these rules applied, when is this validated, and also, what happens
00:46if I need something a bit more detailed than this?
00:49Let's go ahead and run the application. I'll run the application. I create a new author.
00:57Well, I'm going to create an author called Bo, and Bo should actually violate those validation
01:03policies, because as I said it had to be at least three characters.
01:06Well, it doesn't seem to have a problem with it.
01:08Now there's the question, can I create courses for Bo?
01:11It looks like I can.
01:12I can jump between those different author objects.
01:15I can even create another author object.
01:18Well, it doesn't seem like the validation had any effect.
01:21Let's quit out of the application. I hit Quit.
01:25Here I get the error message, Property/name/Entity/Author is too short.
01:31So I am getting that validated.
01:33What's happening is that it's only validating when it's trying to save.
01:37And at the moment, the only time it's trying to save is when the application should terminate method.
01:42So, we get the error message and then there's an option here, do I want to quit anyway? Sure, I do.
01:48You might be wondering well, what happens if I do quit?
01:50Well, let me just go, and run the application again.
01:53I can see that I lost everything.
01:56All the object changes that I made were not persisted because we couldn't issue that save.
02:01So, here's the thing.
02:03The managedObjectContext, which is what's doing the save, well it will allow managed objects
02:08to exist when they break these rules, all the validation rules.
02:12It just won't allow them to be saved.
02:14And that's almost certainly what you want, because you often want a bit of time, so you
02:18can create an object and manipulate it into an acceptable state before you save it.
02:23You don't want all these rules to be applied every millisecond.
02:27These validation rules will be applied when we save, when we terminate right now.
02:32But we can also choose to apply them anytime we want.
02:34I'm going to create a little button on this application so that I can call the save and
02:39validate without having to try and quit the application.
02:42So I will just drag a button on here, this is quick and dirty, Ctrl-drag over to the
02:47App Delegate object in my dock because the App Delegate object, if I let go, will have
02:53an action called saveAction where it will try to validate and save.
02:57So let me just change the text on the button here, Validate and Save.
03:02And we'll be able to check it out while we're running through these examples.
03:09Try this one again.
03:11And if we create Bo, I can click the button to just check that. No!
03:16Property/name/Entity/Author is too short. Okay.
03:19Let's change that to Boo, validate again, doesn't seem to be a problem.
03:24So, we are actually calling that validation.
03:27But what if we want to do more custom validation than these kinds of options we're able to do?
03:32Well, we can do that.
03:33We can add validation methods to our custom classes for our entities and have those methods
03:38called automatically.
03:39So, I'm going to quit out of this, and back into the data model.
03:43Let's say that when I create a new course entity, I want the releaseDate to be a bit
03:48more specific than I can write down here.
03:51I'd like to say that when a new release date is entered, it can't be more than 30 days in the future.
03:56Now, I can write that kind of condition in the data model file, I'll have to write a
04:01little bit of code for that. So this is what I do.
04:03I'm going to jump into the custom class for this entity, which is of course Course.m, and
04:09in here, I need a specifically named method.
04:12Now, this will look a little weird if it's the first time you've worked much with the
04:16key value coding protocol.
04:18But what's actually happening is that while I have a generated method, it won't pop up automatically, unfortunately.
04:24But it will have this format, validate, and then the name of one of our properties.
04:31Now, these all begin with a lowercase letter, but we need to begin them with an uppercase letter.
04:36In my case, I'm going to say validateReleaseDate.
04:45This takes two parameters, a pointer to an object reference, or an ID pointer, here, which
04:52I call value and an error object, which will be called error.
05:02Get myself a bit more room to view this properly.
05:07I could use the same method signature and have a validate title and validate author.
05:13Whenever a new entity is created, we try and save or validate, these methods will be called,
05:18passing in the values that we're trying to use, and these will work with any of your custom properties.
05:24You just need to capitalize the property name after the word validate, and have these parameters.
05:29So, this probably looks a little weird because what we're getting is an id *, a pointer to
05:35an object reference, and a pointer to a pointer of an NSError so that's why we're using the
05:40two asterisks here.
05:42The reason for this is so this method can effectively return two values.
05:47Right now, it's just returning a BOOL, that's the official return type.
05:51But we sometimes really want to practically pass an NSError object back out of it as well.
05:56Now, you know that up to this point, when we've been calling methods like save on the
06:01managedObjectContext, or perform fetch, we've been using that ampersand to pass in the address of an NSError.
06:07Well, this is the flip side of that. We're accepting a pointer to a pointer.
06:12So we can actually change it and effectively pass back the location of that, and someone
06:16else can use it later.
06:17Double indirection pointers, which is what we're looking at here, they tend to baffle
06:21everybody at first, so let me just show you the code we need.
06:25I've just added some code here. Let me go through what this is doing.
06:29First off, we're just asking, was there a value passed in?
06:33If it was nil, then there's nothing we can do. Just say return YES.
06:37Otherwise, what I'm going to do is convert that value into an NSDate, and then I've got
06:41a little bit of code here that's basically just calculating and saying is that 30 days
06:46forward from today?
06:48If it is we're going to create an NSError object.
06:51If you've not created NSError objects before, they're a little convoluted to make.
06:55But that's effectively what we're doing.
06:57We're bundling up an NSError with an error message, and then we're passing back the address
07:02of it, and rather we're changing the address of the error parameter that was passed in,
07:08saying if we failed, it will return NO, otherwise it will return YES.
07:12Now let's go ahead and test it.
07:15So, I'm going to select any of these authors, click the Plus button, add a new course, come
07:24over to the Release Date.
07:25I'm going to add one for today, right now, which should validate just fine.
07:30Not a problem there.
07:34And now add one for about 5 or 6 weeks ahead. Again, it's allowing us to make the change.
07:41We can change the objects. It's only when we try and validate,
07:44we've got the problem. Date cannot be more than 30 days in the future.
07:48Okay, I'll change that back to a little less, validate it again. Not a problem.
07:56So, this is how we add custom validation routines to our code by creating these methods with
08:03a specific name and a specific method signature that matches the name of our properties but
08:09contains the word validate ahead of them, passing in a pointer to an object reference
08:14and a pointer to a pointer of an NSError.
08:18The code that creates an error is a little convoluted to write, but you're essentially
08:21going to duplicate it in all your custom validation routine.
08:24So it doesn't end up being that much of a pain in practice.
08:27Now, how you attempt to visually handle these errors, either in iOS or in Cocoa will be entirely up to you.
Collapse this transcript
Working with relationships and delete rules
00:00So currently, we have these two related sense of entities, we have authors, and their associated courses.
00:07Question might be, what happens to a course object when I delete an author?
00:12Well, the answer is it depends.
00:15It all depends on how the relationship is defined in the data model.
00:19So let me quit out of this and go into the data model.
00:23All relationships of course, can be viewed from two perspectives.
00:26I am going to open up my Utilities panel here, that we have right now Author going to Course
00:33and Course going to Author.
00:35We can view this relationship from either perspective, but any relationship that's a
00:39To-Many is the more interesting one here to use as a demonstration.
00:43So let's take the relationship from Author to Course.
00:48So a single author can be connecting to multiple courses, meaning can have a reference to multiple courses.
00:55And those courses, in return, back the way will have a pointer to the author object.
01:00And this is what we're interested in, here, is the delete rule being specified in our relationship.
01:06The default delete rule is nullify.
01:08What that means is if this author is connected to a course, and I delete the author, then
01:14it'll reach into all the course objects that have that author as a property and just set
01:18that particular property to null.
01:21The Author object will disappear, all the Course objects will be updated.
01:24If I click the dropdown, there is another option called No Action, literally do nothing,
01:31don't touch the objects at the other end of this relationship.
01:34So if I delete an author, and I have three courses with a property pointing to that author,
01:40don't touch them at all.
01:41This could potentially lead to inconsistencies in your object graph, and I will admit I've
01:45never needed to use this option. The next one we have is Cascade.
01:49Those of you who have worked with Database Design will probably recognize what this is
01:52about to do, meaning if this Author object references three courses, and I delete the
01:58Author object, those course objects will be deleted too.
02:01So be careful with that one.
02:03And we also have Deny, meaning if I attempt to delete an author who is an existing reference
02:09in one or more courses, the Managed Object Context will stop me, and it will only allow
02:14me to delete an author if there are no course objects listing that author as a property.
02:20So let's go ahead and try Deny here. I'm going to save this and go ahead and run it.
02:25I am going to select my course here and just delete myself as an author.
02:31Well, seemed to work. So let's go ahead and try and validate that.
02:35Here we have the problem, like validation, these delete rules are only enforced on a save.
02:42So items cannot be deleted from property/courses/ entity/author, not the most useful message in the world,
02:48but we know what's going on here. I'm going to quit out of this.
02:52It's going to give me the message. I'm going to quit anyway.
02:55If I open this up and run again, I have got my courses back.
02:59But what it should allow me to do is go ahead, delete all the courses for a particular author,
03:05and then delete that author, and that should validate just fine, and that would be the Deny rule.
03:12Now, one of the issues is, it's completely possible that right now I have multiple courses
03:17that already exist without an author, they just aren't showing up, because the way I
03:21have created this particular user interface screen, it doesn't have any way of showing
03:25us courses that don't have an author because everything here is all driven from the author.
03:30I would need to write another window, or another user interface element, that just shows all
03:34the unfiltered courses from that perspective. But so far, this is a little clunky.
03:40What would be really nice is if we try and do some operation and then validate that
03:45there is a problem here, it may be nice if we could undo the change that caused that problem,
03:50and of course, we'll get into that next.
Collapse this transcript
Implementing undo and redo functionality
00:00Let's add Undo and Redo support to this Core Data Cocoa application.
00:05Are you watching carefully? Because I'm done. You see it's already there.
00:10Our managed object context, the collection of managed objects, our scratch pad, our
00:14beating heart of Core Data already has an Undo Manager built right in, and it's already keeping
00:20track of everything we're doing when we're running this application, it understands our
00:25data model, it understands our relationships.
00:27However, it just doesn't seem like it's working right now, because I don't have anything available
00:33under the Edit menu, and I also don't have the Command keyboard shortcut working, but
00:39this is very simple to interact with.
00:41And when we usually don't want to directly call it ourselves, I'm going to show you how
00:46I could if I wanted to.
00:48So I'm jumping back into the application, I'm just going to drag on a simple button
00:52onto this interface somewhere, and say Undo, and all I'm going to do here is shift into
01:00a system view and manually call it, so we're connecting to our AppDelegate.
01:05I'm going to Ctrl-click from the button.
01:08I want to make sure that I'm doing an action on an outlet, and I'll just call it manualUndo
01:15to create a method here.
01:18Switch back to the standard editor and grab my implementation.
01:26So I've got my method signature here for manualUndo, and this is the entirety of what I'd need
01:32to do, self.managedObjectContext undo.
01:40The undo method takes no parameters, it returns void, as we should always be able to call
01:45undo repeatedly whether it has something to undo or not, and it will be exactly the same for redo.
01:52I'm just going to go ahead and run it and prove that this works.
01:56Let's click one of the authors, and I click to create a couple of courses, I change a
02:01value, and I delete one of the courses.
02:07Go up here to the Undo button, click it once, the course reappears, click it again, click
02:12it again, click it again, you can see the whole stack being measured until we can click
02:17Undo no more and nothing happens. So redo is exactly the same.
02:22There is also a rollback method, which takes the managed object context back to its last
02:27saved state, but Undo and Redo are much more common.
02:31Of course we don't usually call undo with a button, so let me actually get rid of that
02:36and do the normal style of calling this.
02:39We use the menu item, or the keyboard shortcut, Command+Z or Command+Zed, whatever pronunciation
02:45you prefer for that particular letter.
02:47And I saw that when we're running this application that menu item is grayed out.
02:52What we want to do is have this automatically appear and become enabled when there is something
02:57to undo and be grayed out when there isn't.
03:00Well, you see 99% of that behavior is already in place.
03:06If I take a look at the Undo and Redo actions in my menu and open the Connections Inspector
03:12I'll see that they are sending actions over to the first responder, they're hooked up correctly.
03:18If I select Undo and take a look at its attributes, I'll also see that we have the Key Equivalent
03:23in place for Command+Z or Command+Zed, and everything is actually ready to be connected and aware
03:28of an Undo Manager in our managed object context, and the thing is we already have one.
03:34If I jump over to the AppDelegate implementation file, up to the Jump Bar here I already have
03:40a method here called windowWillReturnUndoManager, and all it's doing is returning the Undo Manager
03:47that's embedded already in the managed object context I'm already working.
03:52This is provided for us in a default Cocoa application with Core Data.
03:57But if I were to put a log message in this method we'd see it isn't being called right now.
04:02There is one thing missing to connect all these up properly, and that's over in my XIB,
04:07in my main menu, I need to select the window here, open up my Utilities Panel, and I'm
04:12looking for the Connections Inspector, and making sure that what I have selected is the window object.
04:19The delegate outlet of this window should be connected to my App Delegate so that that
04:25method can be called.
04:27So if I click from here, drag over to the App Delegate object and let go, everything is hooked up.
04:34See this is needed if you create a Cocoa application that's not document-based, and it kind of
04:40strikes me as an omission, it's one of those things that might change in future releases of Xcode.
04:45So if you have created a new project, make sure you actually have this problem before
04:49you fix it, well that is how to fix it.
04:51So I'm just going to save that, go ahead and run it.
04:55First I'll change the value, add a new course, change another value in there, and go up here,
05:04and I can see that I've got the Undo menu, I can start to back those out, we've got a
05:09stack, now Redo has become available as well, and not only that but you'll find it's fully
05:14aware of the relationship. So if I create a new author, create multiple courses for
05:21that author, I can still have that Undo stack all the way in and out of that just hitting
05:27Undo repeatedly, or Command+Shift+Z for Redo.
05:31And the managed object context is keeping track of all of it.
05:35Now for Cocoa applications this is already turned on and provided by default.
05:40On iOS projects the managed object context still has an Undo Manager property, but that
05:45property is nil by default, it's not instantiated.
05:49Now mainly that's because undo is not as expected on iOS, it's turned off by default in an application,
05:57because it's usually implemented as the shake gesture and many users still don't know about
06:01that as an option, but you can certainly instantiate a regular NS Undo Manager for your managed
06:08object context in iOS, and just do what we've done here, arrange to call undo and redo on the managed object context.
Collapse this transcript
7. Store Types and Model Changes
Revisiting Core Data store types
00:00All the applications we have been making are saving to a single physical store file.
00:05We have seen this file a couple of times.
00:07For Cocoa, you can find it by going out to your home folder, then into Library/Application
00:12Support, certainly this is the path if you are accepting all the defaults.
00:17And depending on what you've named that application you'll find an entry, and for me it's under
00:21com.mycompany, you'll find an entry there for the actual saved store file.
00:28Now one thing to be aware of is if you've used multiple projects with exactly the same
00:33name, and same details, for example, a lot of copied projects, say several of the exercise
00:38files, which are just the same project in various saved states, that they'll all be
00:42trying to access the same file path. That may or may not be what you want.
00:47Now for iOS applications, it is also possible to navigate to the equivalent location for
00:53the simulator, although it's a little more tedious to do because the path is not so obvious.
00:59But again, still in my home folder /Library/Application Support, I actually have an entry here for iPhone Simulator.
01:06Now in that you may have multiple folders depending on which simulators you have support
01:12for and what you've deployed to.
01:14I have been using 5.1 for my applications and under that I have got an Applications
01:19folder, inside that I may have one more or multiple folders with the long generated name.
01:26Now I only had a couple of applications installed in my Simulator.
01:30So I only have a couple of folders.
01:31If you have a lot of apps in the Simulator you'll have folders for each of them.
01:35So you'll either need to take a browse through them or you can look by, say, the Date Modified
01:40and have a clue which one was changed the most recently.
01:44But inside a folder for the application here you'll find the app, you'll find the Documents
01:48folder there, and that is the Core Data store file, in this case, an SQLite format.
01:55We've touched on the idea to by default these files are internally using XML if you create
01:59a Cocoa app, or SQLite if you have an iOS app, but that can be changed, and there are a couple
02:06of options for the internal structure of this file, so let's talk about that now.
02:12These are your persistent store types, XML, SQLite, Binary, and In-Memory.
02:18XML is the default option for Cocoa, and the best thing about it is that it is possible
02:23to just open it up and read it, it's XML, it's human-readable, it might not be anybody's
02:28idea of fun, but it is readable.
02:31Now this option is not available on iOS, you don't really have access to the file system
02:36in any meaningful way so having a readable file is not an advantage.
02:40XML is what's called an atomic store type.
02:44This does not refer to the usual atomic, non-atomic option of properties in iOS, we're just talking
02:50more about the idea of what that means in the larger computing programming sense that
02:55an atomic store file is taken as one complete piece.
02:59The impact of this means that any change to this file requires the entire file to be written,
03:05it is atomic, it's treated as one thing.
03:07That's not a problem with the small file, but it would rapidly become a pain if this
03:11is very large, and the tiniest change to an object require the entire thing to be written in full.
03:18So the most powerful and often the best option is to use SQLite this is available for both,
03:25and it is the default storage format on iOS.
03:28We could switch our store type to using SQLite in Cocoa, we'll do that in just a moment.
03:33SQLite is fast, but it's also non-atomic, and what I mean by that is you can make a
03:38change to it without rewriting the entire data file, which makes it a lot faster particular for large stores.
03:45Now one important thing you do not need to know SQLite, you do not use SQLite to calls, to talk to this.
03:51You do not run your own SQL statements on this, you don't import your own SQLite table
03:56into this, you don't touch it, you let Core Data touch it.
04:00The internal structure of this is private, it's not meant to be manipulated by you.
04:05And we have Binary, it's another file format, it is a little faster than XML, although not
04:10human-readable, and it's very fast for small files.
04:13But again, it's atomic, it has to be taken as one thing so any change requires a full
04:17rewrite of this file.
04:19And finally, we have In-Memory. This is not a common one but can be useful.
04:25What it means is these objects only exist in memory, it gives you all the Core Data
04:29features, but no persistence.
04:32That might sound like an odd thing to ask for, but imagine that you are writing an app
04:35that requires up to the moment financial information, or worldwide flight information, or weather
04:40info as it happens.
04:42Your app might be getting that all by calling a web service.
04:45So you're bringing the information in to your application, into your managed object context,
04:51you have your data model, you have relationships, you just don't want to persist any, you don't
04:55want to save that information.
04:57So you're accessing all the benefits of loading into Core Data but without saving it.
05:03This is a more unusual option, but it is an option, and it is also possible to write your
05:08own custom store type, but that's way beyond the needs of this course, and also Apple are
05:13introducing the idea of using iCloud with Core Data, again something beyond the scope
05:19of this course, something that you really wouldn't use without one of these existing
05:22regular persistence mechanisms anyway.
05:24And the most, the most flexible store type is SQLite, but you may need to convert one
05:31store type to another.
05:32Say, taking a Cocoa application that started with XML and converting it to use SQLite instead.
05:38We'll see how to do that next.
05:43
Collapse this transcript
Converting store types
00:00I have this new, although straightforward, Cocoa application I've just written and started
00:05to use adding some simple objects to it.
00:07Now I have accepted the default store type that Xcode has provided for this application,
00:12which of course, would be XML because it's a Cocoa app.
00:15If I wanted to check then I would look in the AppDelegate file in the method that creates
00:20the persistentStoreCoordinator.
00:23Because it's here that it's looking for the file address, making sure that something is
00:27there, creating a directory, if it's not, and this is the part we are really interested
00:32in is adding the persistent store with the type, NSXMLSStoreType.
00:37Now if I had not started to actually use this application and add data to the store, I could
00:42just go ahead and change the store type to one of the other, say NSSQLiteStoreType, and
00:50go ahead and run that, we're going to have a problem.
00:54The file couldn't be open because it isn't in the correct format.
00:57Well, that kind of makes sense.
01:00Now seeing as I'd only created three simple objects.
01:05It wouldn't really be a big deal for me to just delete the store file and let it re-create,
01:09and that would absolutely work, but let's imagine there is a little bit more data than
01:14I would want to delete and then re-create. So we'll talk about how to convert it.
01:18I'm going to undo the changes that I made here.
01:21I have to change this back to the XMLStoreType so that we can do a proper migration of the data.
01:27Now when we talk about migrating in Core Data, it really has two meanings.
01:31One is to be able to convert from one store type to another--like XML to SQLite or SQLite
01:37to Binary--and it can also mean to convert from one data model to another. If your store
01:42was created with a different data model.
01:45We are talking about doing the conversion here, changing the store from XML format to a SQLite format.
01:51So I'm going to migrate it.
01:53I have to leave these lines of code here because this is what's actually grabbing hold of the
01:58existing XMLStoreType.
02:00So everything I do is actually going to be after that.
02:03So it's way towards the bottom of this persistentStoreCoordinator method.
02:08I'm going to put in some temporary code.
02:10I don't want to add a migration feature to this application, I just want to run some
02:14code once to move it over and then start using the new SQLite version.
02:19So here's how I do it as an ad-hoc, one-off conversion.
02:25First, I'll create a new NSURL object to hold the new path that I want.
02:29I am not going to try and replace the existing one, it's a store data.
02:33I am going to make a new file, which I'll call SQLData.
02:39The name doesn't really matter, you can call it whatever you want.
02:42You just need to able to re-create it when we are looking for it later.
02:46Next, I am going to add a line that will let me grab the old xmlStore from the persistentStoreCoordinator,
02:54this is what we are actually creating on lines 85, 86, and so on.
03:02And now I can tell the persistentStoreCoordinator to migrate from one to the other.
03:06So I am calling the migratePersistentStore: toURL: options withType error.
03:14When I'm really interested in the same we are migrating the old xmlStore that I created
03:18a handle on line 92 to the new URL with the new type of NSSQLiteStoreType, that should do it.
03:29Now just a simple log message.
03:34That seems like it should be something I should be able to do without first loading the xmlStoreType
03:38but I can't, I have to load it, then convert it. So I am going to go ahead, run this once.
03:44I can see the message popup in the background that this has migrated, so we at least hit that code.
03:49And then importantly I'm going to quit, because the code that existed was then both loading
03:54in the XML version and creating a new SQL version, I want to go back to it and basically
03:59change everything around.
04:00I can now get rid of this code, or if I wanted to keep it around for later just comment it
04:05out with the Command+Forward Slash.
04:08And then I'm just going to change the existing code that we had here to bring in the old
04:13XML version and change that to sqldata, that's the new path, but also importantly make sure
04:19that it's not trying to load as XML, but as NSSQLiteStoreType.
04:25And just if I wanted to double-check even before I ran this I could jump to my Finder,
04:29go out to the lucky old Application Support, find my ConvertedDemo where I should see two files there.
04:38The storedata one, which is in the lightweight XML format, and the sqldata one, which has
04:42a little bit more weight, just because we're really at sqldatabase, so it's 20 K over 2,
04:48although that difference won't be as big once we start growing this.
04:52That should be in, I am going to save this and run it.
04:55It looks like we can add new options here.
05:03Quit out of the application, open it up again, and we're keeping and persisting all that
05:08data, successfully using the new store type.
05:12Now if I look back on my Finder window I still do have the old one hanging around.
05:17It's not really important, I could get rid of it, if I wanted to, I don't need it anymore,
05:21but I also have access to the new SQLite version, could even take the store file over to an
05:27iOS application as long as we had an exactly matching data model.
05:30In fact, let's see that next.
Collapse this transcript
Preloading default data
00:00Earlier in the course we created this iOS application for editing these course entities.
00:06We'll click the Plus button, and fill out some data, save it, there we go.
00:10But what if we'd wanted this application to be preloaded with data?
00:14So the first time anyone opens the application, it's got a bunch of courses already inside.
00:19Well, I can't just go through and create several entries and be done with that, because the
00:25store part of this, the actual file that's being saved is not part of the project assets,
00:30it's stored purely at a user level.
00:32So I could create a hundred courses in here, but as soon as I install this program on another
00:37device, I'd have nothing. We have a few options for preloading data.
00:41One, we could write some one-time only code to detect if it's the first time the application
00:46runs, instantiate a bunch of objects, and immediately save them to the new store file.
00:51Now this is possible, but it's going to interrupt that initial startup, and you'd only ever
00:55want to do this for small, or dare I say tiny, amounts of data.
01:00Now similar idea is you might include a Plist or CSV file as a resource with your project
01:06and then write some one-time only code to iterate through and instantiate all your objects based on those.
01:12Again, not something you'd want to do with a lot of data because it would slow down your
01:17app, particularly on that all-important first time that the user tries it.
01:21Now what's a better option is that we provide a Core Data store file already loaded with
01:27data as an application resource.
01:29And when we first run the application if we find there is no store file already there,
01:34instead of just making an empty one, we'll copy that asset across and make it the local store.
01:40That of course, must match the store type and the data model defined in the project, so let's do that.
01:47So how do I get that store file? Well, we could do this a couple of ways.
01:50One I could actually use the iOS app here, painstakingly adding a bunch of new courses,
01:55and then navigate out to the file path of the Simulator and grab the store file that it saved.
02:01That's kind of a little tedious, so let's not bother with that one.
02:05Well, another option that I might use is just make a simple Cocoa application, like the
02:10one I am looking at here from the previous exercise just using Cocoa bindings, and use
02:15this to do a front load of data as it's much easier to type multiple entries into a Cocoa
02:20app rather than in an iOS Simulator, or even you could create a small importer helper application
02:27if we're bringing all the data in from somewhere else.
02:30And that means I wouldn't have to have that import code sitting in my main application.
02:34But importantly, whatever is going to create the store must use the same data model and
02:39the same store type. If there is any mismatch this won't work.
02:43So the best idea is you've already configured the data model correctly in your main application,
02:48and you'll just copy that data model into your helper application.
02:51I've got a bunch of entries that I created here, and I had gone out to the Finder to
02:57find the actual entry for this, again, of course, this has to be in SQLite if we are bringing
03:01into an iOS, SQLite application.
03:04But I had the ConvertedDemo.sqldata format, which I have just dragged onto my Desktop there.
03:10That's provided in the exercise files if you want it, and there is just a bunch of entries here.
03:15But I don't need the app, I just need the store file.
03:20So what I'll do is I'll go over to the application that I want to preload a bunch of data and
03:27then navigate to where that SQL data file is. I'm going to drag it into that application
03:33so I can have it as a resource. I'll drop it into Supporting Files.
03:39When the window pops up, yes, I want to copy them into the Destination Group folder, absolutely,
03:44and importantly, I want to make sure this is added to the target, it is a part of our deployed
03:49application, Finish. So this file is called ConverterDemo.sqldata.
03:55It doesn't really matter what it's called, we just need to remember what that name is
04:00so that we can access it in the code in just a moment.
04:03So here's what I need to do.
04:04I am going to go into my AppDelegate here, and I'm interested in the persistentStoreCoordinator
04:10method, that's going to have everything that takes care of loading it, dropdown into the
04:15actual method rather in the Property, and give myself a bit more room.
04:20So right now what it's looking for is a file called CDCourses.sqlite.
04:24Well, I know that what I want us to look for a file called ConverterDemo.sqldata, if that
04:31file doesn't exist on the file system for this iOS app then we are going to copy it across.
04:36So I am basically going to ignore that path and just replace it with a new file that I want.
04:47So creating a *storeURL, and then we can ask if that actually exists on the local file
04:51system of the iOS device, whether that's the Simulator or real device.
04:56So on line 108 I grab the default file manager so we can ask if certain files exist, and
05:05that's what I'm doing on 109, does that file already exist with the name ConverterDemo.sqldata?
05:11Because if it doesn't exist, we know that this is the first time through that we've
05:15run this application, or if it's not the first time someone has at least deleted the application
05:20off that device or off the Simulator.
05:27So then I'll create a new NSURL this just grabbing the location of that embedded resource
05:32on the machine, this is what we used to grab hold of it, ConverterDemo with the Extension sqldata.
05:43And as long as something exists from that let's copy it across and do copyItemAtURL,
05:51the location of that resource, and copy it to *storeURL, the address that we want it to
05:58be at, and I am just going to provide NULL for the error.
06:03Save that, go ahead and run it, and there we go, the information automatically preloaded.
06:13Now the only thing to be aware of, here, is if you had brought in a file that actually
06:17had the same name as the application, and you'd already been working with it.
06:21You might need to clear everything out on the Simulator so that we load it correctly the first time.
06:27So the easiest way there would be either delete the app from the Simulator or do a reset content
06:32in settings and redeploy it, and then we'll have no store, we'll copy it across.
06:37And next time we run the application it will detect that that store file already exists,
06:42so we won't need to copy the asset this now is our store for this application.
Collapse this transcript
Dealing with schema changes
00:00Something you may well have run into already in your experimentation with Core Data, what
00:05happens if you change your data model after you've begun to actually use that data model
00:11in a running application?
00:12So here I have this simple Courses application I have created, a bunch of objects here.
00:17And let's say now I decide that my data model needs a tweak.
00:20We need a bit more information.
00:22So I'll go into the Data Model file and just add a simple new attribute, I'll call it category
00:28and make it a String. Save that and run the application again.
00:34We are immediately going to run into a problem, basically the thing will blow up.
00:40It will give me a whole bunch of information, but the thing I'm most interested in is the reason.
00:46The model used to open the store is incompatible with the one used to create the store.
00:51That's a pretty clear error message, and it shouldn't really surprise us.
00:54We are changing the basic format of how this information has saved, which means we are changing how it's loaded.
01:01It's the equivalent of changing a relational database schema, or a file format, in an existing application.
01:07You're going to run into conflicts if you do this.
01:10So I'm going to stop this and go back into the application.
01:13The question is did we irrevocably break everything?
01:16Well, let's see what happens if I go and back out that change, remove that attribute, save
01:22this again, and run it.
01:25We do seem okay, so we've recovered from that one, but it's not really a good idea to start
01:30changing the data model with the hope that you can undo your changes exactly.
01:34I might have played around with this, but I realized maybe that my new data model is
01:38more important than the handful of test objects I have made so far.
01:42Well, of course, I could just go and delete the store file and start again, either resetting
01:47the simulator or clicking and holding to delete the application.
01:51You can try and find the store file and delete it yourself, but that would be much easier
01:55to do with Cocoa Applications than on iOS.
01:57And because it's a bit more difficult on iOS, you will find that there is a little commented
02:02out code in the persistent store method that can help you, because it's very common just
02:08to need to delete the store when you're working with an iOS app just while you're in early development.
02:14I'm not going to do that right now, but I'll show you where it is if you wanted it.
02:17So, jump to the persistentStoreCoordinator method of this iOS Core Data app, and you
02:23will find some code commented out here.
02:25Now, all this code is in the area that would be called if there's an error, if there's
02:30a problem loading, or creating that persistent store.
02:34And here is the option that I'm talking about here just a little reminder there.
02:38If you encounter schema incompatibility errors during development, where you don't really
02:42care about what's in the store file, you can just delete the existing store, so you should
02:47grab that line of code and put it in an uncommented out section.
02:53Now bear in mind, that you don't want to run this line every time the application loads
02:56because that would be a real problem, you just continually delete the store.
03:00So I could paste it in here meaning that we'd only hit this brace if we actually have a
03:06problem if there's an error that's caused.
03:09Though the way I have done it here, we would actually throw an error so it would abort,
03:12so the first time I'd run it, and there was a mismatch, it would delete the store, I'd
03:16need to quit and run it again to take the new store into effect.
03:20But I'm not going to do that right now.
03:22But while developing, it can be a useful piece of code to know that it's there.
03:26If you want to make any significant changes while in the early stages of development just
03:30be prepared to throw your store file away, and if the data is minimal, or it can be rebuilt,
03:36that is by far the best option.
03:39But at some point, we're going to need to make a change to a data model that's in use,
03:43and we won't want to throw all the data away. So we're going to see how to do that next.
03:48Instead of just changing the data model, we are going to find out where you can create
03:52multiple versions of our data model, and then describe to Core Data how it should migrate
03:58data across those versions.
Collapse this transcript
Exploring lightweight migration
00:00When you have a data model that's in active use, meaning that you're creating and saving
00:05data based on that data model, and you can't get rid of that data, but you need to make
00:10a change to the model,
00:11we do that by creating a new version of that data model, not just by changing the old one
00:16and hoping for the best.
00:17We need the old version to still exist so that we can describe to Core Data how it should
00:22convert the old data across to the new format.
00:26So let's say we go back to this idea of wanting to add a new attribute to an existing entity.
00:32Well, rather than just add it in here to my Attributes list, what I'm going to do--with
00:37the datamodel file selected and open-- I'll go up to the Editor, and we have an option
00:42here to Add a Model Version.
00:45So in here it's asking me, what do we want to call it?
00:47Version name, CDCourses 2, Based on the original model, CDCourses.
00:51That's fine, the numbering system is actually whatever is meaningful to you because Core
00:57Data will use its own internal numbering system based on some hashes inside, but this will do.
01:02I'll click Finish, and if you notice over here that what it's actually done is expand
01:07our regular xcdatamodeld file.
01:09And really tell us what's going on a little bit, which is under the hood, it's actually
01:14a group, and there is a couple of files inside here.
01:17So we have the new version 2 data model and the old data model.
01:21Obviously, both of them are exactly the same right now.
01:24The old one currently has a check mark on it, and that's saying that this is the active one.
01:30So, for example, you may want to spend some time working on this new version of the data
01:34model before you actually say now it should take effect.
01:38What happens to change that little check box, a lot of people would imagine that it's maybe
01:42a right-click button, but it's not.
01:44You take the top level element, the part of the group, and you'll find that in the File
01:49Inspector for that top level element you have a little Current dropdown box.
01:54It is a version of data model, what is the one that we're interested in using.
01:59So we'll change to the second one in just a moment.
02:02So let's say we select the second data model here.
02:05Again, being careful which one you're changing, and then I'm going to add this new attribute.
02:09Again, called Category, and this is going to be a String.
02:15I could even provide, say, a bit of default data for this.
02:18Let's say the Default Value is Developer.
02:22So what I can then do is come to the main data model, so selecting the top level element
02:27and say that should now take effect. We should now be using the second one.
02:31And you might be thinking, okay is that it?
02:33Now we got the check box on version 2, version 2 seems to have category.
02:38Let's go ahead and run it.
02:41Well, we still get that same immediate explosion just because we made a different version of
02:49the data model doesn't mean that we didn't change it, we still changed it.
02:53I'm getting exactly the same reason here.
02:55The model used to open the store is incompatible with the one used to create the store.
03:00But we might be thinking these changes that I did weren't that big.
03:04If I quit out of this and just go back and look at them surely Core Data can figure out
03:10some simple changes like adding an attribute to an entity.
03:14Well, actually it can.
03:16We can turn on something called Lightweight Migration, and that's us asking Core Data
03:21to attempt to infer how the old version matches to this new version.
03:26A Lightweight Migration is a phrase almost belittle as what this is capable of, it's very, very good.
03:31It will let us do things like add and remove attributes, add, remove, and rename entities
03:37in our new version of the model.
03:39It will also let you add, delete, and rename relationships, change the relationship type
03:43from To-One to To-Many, and it will migrate all of this automatically.
03:48Although if your User Interface reference is something that you've removed, or renamed,
03:52you will not surprisingly need to make some changes.
03:55And if you've generated a custom class from your entity you'll need to either regenerate
04:00it, or edit it, to bring it in line with the new entity definition.
04:04Here's how we do Lightweight Migration, it's an option when we open the persistent store.
04:09So I'm going to leave that version 2 selected, we want that, and then I'm going to navigate
04:15to my AppDelegate into the implementation file and find the place where we're actually
04:19loading it in, which of course will be the persistentStoreCoordinator method.
04:24We don't typically have to do much with the persistentStoreCoordinator, only when we were
04:29trying to effect what's happening when we were opening up a store or closing it, changing
04:33something about it what we need to get involved.
04:36In this example, line 119 is where we were actually adding this store to our application.
04:41We were saying it's a SQLiteStoreType, and this is where we create it.
04:45If there's any errors they will be thrown right here.
04:49I'm showing you this because the secret is in this part, where we're attempting to add
04:54and open this persistent store to connect it to our application.
04:59We have an argument called options that takes a dictionary, and we can pass in a couple
05:04of options to that dictionary to say, hey Core Data, I would like you to attempt to
05:09map that new data across.
05:12So before this is getting called up here I will create a new dictionary.
05:18Seeing as it is late 2012, we could use the new NSDictionary literal format, but if you're
05:23not used to it I'm just going to use the conventional way of doing it.
05:27Use the dictionary with objects and keys initializer, I only need to provide a couple of Boolean
05:34representations here, look a little odd.
05:37The option I'm looking for that should pop up is the NSMigratePersistentStoresAutomaticallyOption,
05:43long-winded, but it sounds tempting and then another NSNumber wrapping a Bool here.
05:51And the second option begins with NSInfer that's the one we want, NSInferMappingModeAutomaticallyOption,
06:01and then Comma, and nil to close that dictionary.
06:04Now we have a dictionary called options with those two values in it.
06:08I'm going to change here to paste in that option there.
06:11Save it and run it.
06:13Now I haven't changed the model back so we're still attempting to use version 2, there we go. It works.
06:20We can go ahead and create new options here, and save them.
06:27We're correctly using the new data model.
06:30And of course, it would be our job now to create new User Interface elements that allows
06:35me to change that new attribute that we've added and whatever else is needed.
06:40But Lightweight Migration is great for letting us change our model without losing all our data.
06:46However, when Core Data cannot infer the mapping automatically, when there's been too many
06:51complex changes between versions, then yes it does get more complex.
06:56You can provide what's called a mapping model, this is actually a new kind of file for a
07:01Core Data application.
07:02I can add a new file and under the Core Data section where we've got Data Model, we know
07:07those, we've got ManagedObject subclasses, we've done those, and there's a new one called a Mapping Model.
07:12That allows you to pick a source, and a destination mapping model, and then do a bit more manual
07:19work about how one should map to the other.
07:22Then there's an object called a Migration Manager, that you can configure with this Mapping
07:27Model and write a little bit of code to convert one to the other.
07:30But these more complex data migrations are beyond the scope of this course, because they
07:35are all different in their own way. So I will refer you to the authoritative source.
07:43Once you go beyond what Lightweight Migration can do you're going to be interested in the
07:47Core Data Model versioning and Data Migration Programming guide.
07:52This will be your guidebook if you need to do more complex migrations involving mapping
07:57models and adding custom code, if you need to execute custom code during the process.
08:01But you will find a Lightweight Migration can actually take care of a lot of the issues
08:07that you're going to run into.
Collapse this transcript
Conclusion
Final thoughts: Back to the stack
00:01What I wanted to do with this course is take you through the things that you will always
00:04need in every Core Data application you make, managed objects, and managed object models,
00:10managed object context, fetch requests and fetch results controllers, validation, undo,
00:15and redo, importing, and migration.
00:17At the beginning of this course I showed a diagram of interrelated objects, which should
00:23hopefully make a lot more sense now, because you have all the pieces in place.
00:28We know that we start with the data model defining our entities, the descriptions of
00:33our attributes, our default values and relationships.
00:37Those entities will be gathered together into a managed object model, that's what the data
00:42model file will be turned into when our application runs and those entities used to create our managed objects.
00:50We have the all important managed object context.
00:54The beating heart of Core Data, the scratchpad, or workbench, for all your managed objects.
01:00It's the context that contains them all, it's the context that we use to perform fetch requests
01:04and saves to undo and redo.
01:07And our context is connected to the persistent store coordinator, and that doesn't need a
01:12lot of input from us, but it's what will connect us to the actual persistent store itself,
01:17the store file, whatever format that might take XML, SQLite, Binary, or even in memory.
01:24Now of course, it can get deeper but this is Core Data, this is the Core Data stack, it's
01:29everything Core Data needs to work.
01:32But as you continue experimenting with Core Data there is a few things that you're likely
01:36to encounter that I wanted to briefly talk about. these are things not every app will
01:40need, but occasionally yours might. You'll hear about ideas like having multiple persistent stores.
01:47There is nothing that limits you to just one store file, that is the default, and that's
01:52what most apps will need, but Core Data supports using multiple separate store files. You could
01:57have multiple stores that share the same data model or stores with completely different data models.
02:03Couple of reasons for this, one you might have separate stores for different users or
02:08separate stores that the application opens up one at a time. Think of if you've used
02:13iTunes libraries, large collections of data, but you're allowed to close one down and then open another.
02:19They share the same schema, but they are different stores of data.
02:24But even with multiple stores you don't have to have the same data model, you could have
02:27separate stores using different data models.
02:30One reason for this might be one store that's configured for user data, and you create one
02:35model for all the user data, things that change and are volatile whereas you have a completely
02:40different store for say some predefined and included object assets, and that would allow
02:45you to manage them as two separate files.
02:48Now neither of these you have to do, nothing is required. All you should take away from
02:53this is knowing that flexibility is available should your app require.
02:58Now the same idea is we can have multiple managed object contexts.
03:03All the default applications just provide one, but if we're taking the idea of a managed
03:08object context as being like an intelligent workbench, or scratchpad, well you can provide more than one.
03:15Say if you have very complex object creation with a lot of dependencies, you might create
03:19a separate managed object context, or scratchpad just for that part of your app, which keeps
03:26its own Undo Manager and can contain multiple objects separate to the main context.
03:31You might use it to create small object graphs then save those from the secondary context
03:37and load them into the main context.
03:39But know that managed objects are always created in a particular managed object context, so
03:46there is no such thing as the same instance in both, although you can fetch multiple copies
03:51of the same object into different contexts, but the contexts themselves are always saved
03:56and handled independently.
03:58Again, not required, but it is an option that you can use, and hopefully you're getting
04:02the idea that all these objects we've being given are actually quite flexible.
04:07And there is a term you might come across called Faulting in Core Data.
04:12The good thing about faulting is you're already doing this.
04:15Core Data is already doing faulting for you, it's a way of efficiently handling memory.
04:20A way of doing a lazy loading. But this is what it formally refers to.
04:25Let's say that I'm fetching a hundred authors out of the store to show in a table view,
04:30but in our data model all these authors have too many relationships to multiple course objects.
04:37So instead of a hundred objects that entire object graph may be much, much bigger.
04:42And here's the thing, I don't actually need Core Data to retrieve all that data at once
04:47if I'm only interested in the office.
04:50Now Core Data is actually smart enough to not fetch everything.
04:54It understands that the author objects have references to the course objects, but if I
04:58haven't accessed them yet the course objects won't have actually been fetched.
05:03Core Data is waiting for me to need them.
05:05This is faulting and these are considered faults.
05:09All these author objects will have a property that refers to them, but that probably doesn't live yet.
05:15As soon as we attempt to access that property of the author object to get to that course
05:19object, Core Data will immediately fetch this first. This is called firing the fault, and
05:25you don't have to do anything, you just behave as if everything is there and available for
05:30you all the time, it's completely transparent to you as a developer.
05:34However, in more complex scenarios you can affect it.
05:38There are things that you can do that affect faulting, there's things like batch faulting and pre-fetching.
05:43For example, if you knew that you should go and grab all these course objects, you can
05:48do some pre-fetching to make sure the Core Data does go ahead and grabs them all.
05:53However, be aware of faulting, but know until you actually run into these problems don't
05:59worry about faulting, it's there already, it's already happening.
06:03But for more on faulting, batch faulting, pre-fetching, all of these advanced options,
06:08and more, the document you should be keeping handy is Apple's Core Data Programming Guide.
06:14This is available from developer.apple.com, it's about 200 pages of reference on all the Core Data options.
06:21A lot of things we've seen and a few advanced features.
06:24So while there are other things we can do, and there are advanced features of Core Data,
06:28things like multithreading, you should already have everything you need to be able to turn
06:32that Core Data check box on or if you have an existing project a pretty good idea of
06:37what you need to copy across and start to make it work with Core Data.
06:41Thanks for joining me for this course and let us know if there is anything you want to see.
06:45See you next time!
Collapse this transcript


Suggested courses to watch next:

Objective-C Essential Training (6h 35m)
Simon Allardice

iOS SDK Essential Training (2012) (6h 26m)
Simon Allardice


Xcode 4 New Features (1h 58m)
Bill Weinman

Cocoa Essential Training (5h 21m)
Simon Allardice


Are you sure you want to delete this bookmark?

cancel

Bookmark this Tutorial

Name

Description

{0} characters left

Tags

Separate tags with a space. Use quotes around multi-word tags. Suggested Tags:
loading
cancel

bookmark this course

{0} characters left Separate tags with a space. Use quotes around multi-word tags. Suggested Tags:
loading

Error:

go to playlists »

Create new playlist

name:
description:
save cancel

You must be a lynda.com member to watch this video.

Every course in the lynda.com library contains free videos that let you assess the quality of our tutorials before you subscribe—just click on the blue links to watch them. Become a member to access all 104,141 instructional videos.

get started learn more

If you are already an active lynda.com member, please log in to access the lynda.com library.

Get access to all lynda.com videos

You are currently signed into your admin account, which doesn't let you view lynda.com videos. For full access to the lynda.com library, log in through iplogin.lynda.com, or sign in through your organization's portal. You may also request a user account by calling 1 1 (888) 335-9632 or emailing us at cs@lynda.com.

Get access to all lynda.com videos

You are currently signed into your admin account, which doesn't let you view lynda.com videos. For full access to the lynda.com library, log in through iplogin.lynda.com, or sign in through your organization's portal. You may also request a user account by calling 1 1 (888) 335-9632 or emailing us at cs@lynda.com.

Access to lynda.com videos

Your organization has a limited access membership to the lynda.com library that allows access to only a specific, limited selection of courses.

You don't have access to this video.

You're logged in as an account administrator, but your membership is not active.

Contact a Training Solutions Advisor at 1 (888) 335-9632.

How to access this video.

If this course is one of your five classes, then your class currently isn't in session.

If you want to watch this video and it is not part of your class, upgrade your membership for unlimited access to the full library of 2,025 courses anytime, anywhere.

learn more upgrade

You can always watch the free content included in every course.

Questions? Call Customer Service at 1 1 (888) 335-9632 or email cs@lynda.com.

You don't have access to this video.

You're logged in as an account administrator, but your membership is no longer active. You can still access reports and account information.

To reactivate your account, contact a Training Solutions Advisor at 1 1 (888) 335-9632.

Need help accessing this video?

You can't access this video from your master administrator account.

Call Customer Service at 1 1 (888) 335-9632 or email cs@lynda.com for help accessing this video.

preview image of new course page

Try our new course pages

Explore our redesigned course pages, and tell us about your experience.

If you want to switch back to the old view, change your site preferences from the my account menu.

Try the new pages No, thanks

site feedback

Thanks for signing up.

We’ll send you a confirmation email shortly.


By signing up, you’ll receive about four emails per month, including

We’ll only use your email address to send you these mailings.

Here’s our privacy policy with more details about how we handle your information.

Keep up with news, tips, and latest courses with emails from lynda.com.

By signing up, you’ll receive about four emails per month, including

We’ll only use your email address to send you these mailings.

Here’s our privacy policy with more details about how we handle your information.

   
submit Lightbox submit clicked