IntroductionWelcome| 00:04 | Hi, I'm Bill Weinman, and welcome to iOS SDK
and SQLite Building Data-Driven Applications.
| | 00:11 | In this course I will show you how to build
a solid RSS reader for iOS that supports both
| | 00:16 | the iPhone and iPad form factors, integrating
XML data into a clean and simple presentation.
| | 00:22 | I'll explain how to use the SQL database,
which is included in iOS, and display information
| | 00:29 | in a clear, intuitive table view.
| | 00:31 | I'll teach you how to code the necessary
view controllers for your application and how to
| | 00:35 | use the Settings application to provide
a Preferences Interface for your users.
| | 00:40 | Finally, I'll show you how to make a universal app
that runs on both phone and tablet form factors for iOS.
| | 00:46 | Creating an application for iOS doesn't have
to be a difficult experience, so let me show
| | 00:51 | you how in iOS SDK and SQLite
Building Data-Driven Applications.
| | Collapse this transcript |
| Exercise files| 00:00 | The exercise files for this course are
included with your basic lynda.com membership.
| | 00:05 | If you are a lynda.com member you have access
to the exercise files used throughout this title.
| | 00:11 | Copy the exercise files to a location
where you can find them on your system.
| | 00:16 | Make sure it's a location that
doesn't have any spaces in the file path.
| | 00:20 | Some of Xcode's tools do not work
properly if there are spaces in file names.
| | 00:24 | I have copied them to
the desktop on this system.
| | 00:28 | The exercise files are laid out by Chapter with
each folder corresponding to a Chapter in the course.
| | 00:35 | Within the Chapter folder there are
other folders with Xcode projects in them.
| | 00:41 | An Xcode project will have one or more
folders inside of it and a file with the name of the
| | 00:48 | project followed by .xcodeproj.
| | 00:51 | You open this project in Xcode by
double-clicking on the Xcode project file.
| | 00:56 | I recommend that you make a working copy of one
of these projects before you open it in Xcode.
| | 01:02 | You can easily do that on a Mac by holding
down the Option key and dragging the folder
| | 01:07 | into an empty space in the containing folder.
| | 01:10 | When you let go of the mouse button,
Finder will make a copy of that entire folder tree
| | 01:15 | and rename it with a number after it.
| | 01:19 | Then you simply click on it, maybe click on it
again, and Finder will allow you to rename it.
| | 01:27 | And then you can open that project in Xcode
by double-clicking on the Xcode Project file.
| | 01:33 | Some of the Chapters have files in them that end in .txt,
these are text files that contain Objective-C code.
| | 01:41 | You want to open these in a plain text editor
like TextWrangler or BBEdit or another plain
| | 01:46 | text editor that is not a Word Processor.
| | 01:50 | Word Processors add other information, other
invisible information to your code and make
| | 01:55 | it unusable in Xcode.
| | 01:57 | The code in these files will be copied and
pasted in the Xcode so that you don't have
| | 02:01 | to type a lot of code yourself.
| | 02:04 | The exercise files are here to make you a
learning experience easier and more powerful.
| | 02:09 | Be sure to take the time to read the code and
experiment with it, make changes, make mistakes.
| | 02:14 | There is a lot of code here,
take the time to learn with it.
| | Collapse this transcript |
| Course overview| 00:01 | iOS is capable of supporting many kinds of
applications, games, utilities, social networking,
| | 00:08 | entertainment, news, business, education.
| | 00:12 | What do all of these applications
have in common? They all use data.
| | 00:17 | The intent of this course is to show you how
to build a data-driven application that uses
| | 00:21 | an external source of data.
| | 00:24 | In order to best demonstrate the techniques
you'll need to accomplish this, I have built
| | 00:28 | a simple data-driven application
in the form of an RSS news reader.
| | 00:33 | Many applications drive their content or even
their navigation on external data, these are
| | 00:38 | called data-driven applications.
| | 00:41 | The application presented here is a
simple example of a data-driven application.
| | 00:46 | The techniques you learn here will apply to
many applications in many different categories.
| | 00:52 | Data sources today are commonly in an XML
format and iOS provides native support for XML.
| | 00:59 | In this application we'll be reading RSS
feeds as RSS feeds are typical of many other types
| | 01:06 | of XML data feeds. The XML portion techniques
covered here will apply to many different XML data sources.
| | 01:14 | After receiving and parsing an XML feed, you
will often want to store that data on the device.
| | 01:20 | The native format for data storage in iOS
is provided by the SQLite database engine.
| | 01:25 | SQLite is a powerful database that is small,
fast, and uses a single file for storage.
| | 01:32 | SQLite is also supported on many other mobile
platforms, including Android, Symbian, WebOS, and others.
| | 01:39 | It has even been ported to Windows Mobile.
| | 01:42 | The techniques you learn here for supporting SQLite
should be usable from many of your mobile projects.
| | 01:49 | Once you have your data, you'll want
to display that data on the screen.
| | 01:53 | iOS provides a native table view display model,
which is commonly used to display data from a database.
| | 02:00 | Table View is very powerful and flexible,
and you will likely use Table View a lot for
| | 02:04 | you data-driven applications.
| | 02:06 | Often, external data sources provide
links to web pages or other dynamic data.
| | 02:12 | iOS provides a native web view for displaying web
pages with all the power of the internal Safari browser.
| | 02:18 | RSS feeds typically link to a web
page for their associated content.
| | 02:23 | These days iOS is more than just the iPhone.
| | 02:27 | So you'll want to learn how to make a
universal application that supports iPhone, iPad, and
| | 02:32 | Retina Displays, leveraging your code to take
advantage of these platforms while using a single code base.
| | 02:38 | I will also show you how to use the iPad's
split view controller providing side by side
| | 02:43 | a Table View for selecting from the
database on the left and a web view for displaying
| | 02:48 | content on the right.
| | 02:50 | This is all designed to give you the tools
you need to use data in your applications,
| | 02:55 | reading data from an external data source,
storing data in a local database on the device,
| | 02:59 | and using data to provide the
necessary user experience for your audience.
| | Collapse this transcript |
| Application overview| 00:00 | The application we'll be using for
this course is a simple RSS feed reader.
| | 00:05 | I call it BW RSS, and it's
available for free in the App Store.
| | 00:09 | This app is designed to be simple for the
purpose of demonstrating how to retrieve,
| | 00:14 | parse, store, update, and use data from an
external data source on the iOS platform.
| | 00:19 | It's clean, fast, and reliable, and
it's not cluttered with lot of features.
| | 00:25 | Here is how it works.
The main screen has a list of available feeds.
| | 00:29 | You select one, and it
displays a list of items in that feed.
| | 00:34 | You select an item, and it
displays that item in a web view.
| | 00:41 | You may add a feed by pressing the add
feed button, this little plus sign up here.
| | 00:47 | You can enter the URL of an RSS Feed here
or a web page that has a link tagged in the
| | 00:52 | header with a link to the RSS Feed, this
is sometimes called RSS Auto Discovery.
| | 00:57 | Once you've added a feed,
you can use it immediately.
| | 01:10 | You may delete a feed at any time by pressing
the Edit button and selecting the red Delete
| | 01:17 | icon of the feed you want to delete,
or you may delete an item by swiping it.
| | 01:27 | As new items come in on each feed,
older items are automatically deleted.
| | 01:34 | There's a setting available in the
Settings App to select the maximum number of items
| | 01:39 | retained by the feed.
| | 01:41 | BW RSS is a universal app, and it works
especially well on the large iPad screen.
| | 01:48 | It uses a split view on the iPad to take a
maximum advantage of the iPad's large screen
| | 01:55 | while conforming to Apple's
human interface guidelines.
| | 02:02 | As we go through the course you'll see how
these techniques were coded and how you can
| | 02:06 | apply them to the applications that you've
built for your own purposes and for your clients.
| | Collapse this transcript |
|
|
1. An iOS TestbedPrototyping in a testbed| 00:00 | Sometimes in the course of your work as
a programmer you need to try a snippet of
| | 00:04 | code or test out an idea or even write a simple test
suite for a class or a library that you're writing.
| | 00:10 | For these situations it can be helpful to
have a small, simple, flexible base of code
| | 00:14 | to use as a test bed to test your ideas in.
| | 00:18 | Here we have such a test bed, and I call it
Testbed, and you'll notice if I run this in
| | 00:23 | the simulator it displays some messages
up on a text view here on this iOS Device.
| | 00:32 | If we look at the code here I have a simple
runTest function that puts out messages using
| | 00:37 | this message function, Testbed version and
for id in this array I have a little message
| | 00:44 | that says object is with the string for
each member of the array and right here we have
| | 00:50 | Testbed Version 1.0, object is Klaatu, barada,
nikto, and that's exactly what we have there.
| | 00:56 | So you can see that this is
very simple to test code in.
| | 01:01 | Now, you could leave the stimulator alone,
and you could use NSLog, and that's okay,
| | 01:05 | and many people do that--and you can certainly
do that if you want to--but it's nice to have
| | 01:10 | the log for errors and the stimulator screen is a
lot less cluttered, and it's easy to read this way.
| | 01:16 | So, this Testbed is easy to build and in fact
I've already done a lot of the work for you,
| | 01:21 | so let's take a moment out from our busy day and
build a little Testbed to experiment and learn with.
| | Collapse this transcript |
| Building the view controller| 00:01 | The first step in building our Testbed is
to create the view controller in interface builder.
| | 00:05 | So let's create a project
in Xcode and get started.
| | 00:09 | Let's start up Xcode here and under the
File menu I'm going to select New > Project.
| | 00:16 | Under iOS and Application I'm going to
select Single View Application and press Next.
| | 00:22 | The Name of our project will be Testbed,
and I leave the Organization Name alone.
| | 00:29 | Company Identifier, example.com, you'll notice
that's in reverse domain notation, and you'll
| | 00:34 | just use your own domain name, or
you'll make something up for it.
| | 00:40 | Our Class Prefix is Testbed, that's used
for building the Class Names and Devices just
| | 00:47 | the iPhone, and we're going to Use Storyboards,
Use ARC, and we do not need Unit Tests for
| | 00:53 | this, so I press Next.
| | 00:56 | This is in our exercise files Chap01 and
Create, and there is our Testbed application.
| | 01:05 | So before we can create the view controller and interface
builder we need to create an outlet in the code.
| | 01:11 | So I'm just going to go into the code here, and I'm going
to close that on the right so we can see the code.
| | 01:17 | I'm in the .m File because we're going to
create the outlet as part of the private interface,
| | 01:22 | that's because we don't need it exposed
to the public, there is no property, and it's
| | 01:27 | not in the header file, it's just
going to be right here in the .m file.
| | 01:31 | Under interface I am going to put in some
brackets and create the outlet right here.
| | 01:36 | IBOutlet UITextView, because this is going to
be a text view, ViewController and an asterisk
| | 01:45 | to make it a pointer because objects in
Objective-C are Pointers, and I'm going to
| | 01:50 | call it TextView like that and a semicolon
and pressing Command+S on my Mac keyboard
| | 01:55 | to save this, and we're done.
| | 01:56 | You notice it creates this little circle
over here that we're going to use as an outlet,
| | 02:00 | and we'll get to that in a moment.
| | 02:02 | Now I'm going to up here to the Storyboard,
and you'll notice--I'm just going to minimize
| | 02:07 | this controller thing over to the left there
because we have limited real estate here on
| | 02:12 | this display for the purpose of the recording--you'll notice
down here at the bottom it says Testbed View Controller.
| | 02:19 | I'm going to bring the inspector back on
the right and show the attributes Inspector.
| | 02:26 | You notice there's nothing there yet
because I haven't selected anything yet.
| | 02:30 | Under my objects here, so you want to make
sure that your Objects tab is selected, under
| | 02:35 | Objects I'm going to select
Data Views, that's what we want.
| | 02:38 | We want the TextView, which is right here,
I'm going to grab a TextView, and I'm going
| | 02:43 | to drag it on to my View Controller and
just get it all lined up just right there.
| | 02:48 | And now under my Inspector you'll notice
that it's got text in it, so I'm just going to
| | 02:52 | select all of that and delete it.
| | 02:53 | I'm going to press Enter on my keyboard, and
you see all that text goes away in the View.
| | 03:00 | I am going to uncheck Editable because we
don't need this to be actually Editable.
| | 03:06 | Now I'm going to bring up my Assistant editor, and
you'll notice that it brings up the TestbedViewController.h.
| | 03:13 | We actually want the .m file so I'm going
to select the .m File here because there is
| | 03:18 | outlet, and I just drag that over to
the View Controller, and we're done.
| | 03:25 | So the View Controller now, if I select my
View Controller and go over here to the Inspector,
| | 03:29 | you'll see that my TextView is in the
Referencing Outlets, and that's exactly what we want.
| | 03:35 | So now we've created the View Controller and all
that's left is to plug in the code to make it work.
| | 03:40 | So I'll save this, and we'll
plug in the code in the next movie.
| | Collapse this transcript |
| Coding the testbed| 00:01 | At this point we have built the text view
in the interface builder, and we just need
| | 00:04 | to plug in the code to make the testbed work.
| | 00:06 | The results of what I did in the previous
movie are in this Testbed-02-done folder,
| | 00:13 | and that's actually our project folder.
| | 00:15 | And so I'm going to take that folder, and
I'm going to copy it, and I'm going to paste
| | 00:18 | it into this Chapter 1 folder, and I'm
just going to rename it to Testbed-O3, because
| | 00:26 | this is Movie 3 in this chapter.
| | 00:28 | And I'm going to open the project by
double clicking on Testbed.xcodeproject.
| | 00:32 | And that will invoke Xcode,
and it will open the project.
| | 00:39 | And I can navigate over here to Testbed View
Controller.m, and I'm going to close the Inspector pane.
| | 00:48 | I'm actually going to close the Assistant
Editor as well and just come over here to
| | 00:52 | the Plain Editor, and that
gives the maximum amount of room.
| | 00:55 | You're on this limited real estate.
| | 00:56 | You can leave all that stuff open if you
like on your screen, because you probably have
| | 01:01 | a lot more room on your screen than I do.
| | 01:03 | So, this is our whole
view controller class here.
| | 01:06 | And so, the first thing I'm going to do is I'm going to
bring in some library code from our exercise files.
| | 01:12 | So under Libraries here, I'm going to grab
this BWUtilities folder, and I'm just going
| | 01:17 | to drag it over here into our project.
| | 01:19 | You see I'm dragging it where it says Testbed,
not in any of these other places just right
| | 01:23 | there in that Testbed folder,
and I'm going to let go of it.
| | 01:26 | And you'll notice here in Xcode,
I get this little dialog box.
| | 01:29 | I want to make sure I select copy items, and
create groups for any added folders and Add
| | 01:35 | to targets has to have Testbed checked.
| | 01:37 | So, what that does is that it copies the
libraries from BWUtilities folder into our project.
| | 01:46 | If I don't have this checked, then it won't
copy, it will just use it in place, and that's
| | 01:51 | not really what we want.
| | 01:51 | We want to copy it into the project, and I
Create groups for any added folders, and you'll
| | 01:57 | see what that will do here in a moment.
| | 01:59 | It will create a group that looks
like a folder in this display here.
| | 02:03 | And Add to targets means it is going to
get compiled into this Testbed target, and we
| | 02:08 | need that in order to be able to use the code.
| | 02:10 | So, I'm going to say Finish, and there's our BWUtilities
folder and just has this BWUtilities.m and BWUutilities.h.
| | 02:18 | And all this is a few functions
that are useful for our Testbed.
| | 02:23 | Here's the message function and our alert message
so we can display alerts, flattenHTML trim string.
| | 02:31 | These are things that we
use throughout the project.
| | 02:33 | We're also going to use them in the
final RSS reader project, or at least some of them.
| | 02:38 | And then in the h file, we have our interface.
| | 02:41 | Now, it's not actually a
class in the strictest sense.
| | 02:44 | It's not using object oriented techniques,
these are all individual functions, because they
| | 02:49 | are actually more useful that way, and
the way that we are going to be using them.
| | 02:53 | But the h file exposes this interface to rest
of the codes so that we can actually use the
| | 02:58 | functions in the .m file.
| | 03:00 | So, I'm going to close that group.
So, that's the group that we created.
| | 03:04 | It looks like a folder in this
interface, but it's actually called a group.
| | 03:07 | Now I'm going to come over here in the TestbedViewController.h,
and I'm going to come down here, and I am
| | 03:13 | going to say import and in the double quotes
I'm going to say, "BWUtilities.h", and you
| | 03:19 | see the code completion is right there already.
| | 03:22 | And I'm also going to come down here,
and I'm going to create a static string.
| | 03:28 | And I'm going to make it constant.
I'm going to call it kTestbed.
| | 03:33 | So, using a k at the beginning of a
variable name means it's a constant.
| | 03:38 | That's a convention that's
often used in Objective-C.
| | 03:41 | And kTestbedVersion equals
and 1.0, and a semi-colon.
| | 03:50 | I'm going press command S to save this file
and then I'm going to switch to the .m file.
| | 03:55 | So now, we've included our utilities
when we created a string for our version.
| | 04:00 | And under viewDidLoad here, I'm going to
just start adding a little bit of code.
| | 04:04 | First time I'm going to declare an external
variable, extern, and it's a UITextView, and
| | 04:13 | it's a messageTextView.
| | 04:16 | And you'll notice that that variable is actually
declared in our BWUtilities.h, and there it is.
| | 04:24 | So, what I'm going to do is I'm going to
populate that variable so that the utilities knows
| | 04:29 | where our TextView is so that it
can display stuff in our TextView.
| | 04:33 | So, I'm going to say,
messageTextView = textView.
| | 04:40 | So, that tells the BWUtilities where our
textView is so that it can actually post messages to it.
| | 04:47 | And I'm going to set the font in our textView,
so it sets it to a system font of size 12.
| | 04:58 | And I'm going to run a function called
RunTest, which we have not declared yet.
| | 05:01 | So, I'm going to say self runTest.
| | 05:07 | And we're going to get a littler error here,
because it's not declared yet, and I'm going
| | 05:09 | to come up here above it,
and I'm going to declare it.
| | 05:12 | I'm going to say (void) runTest, and now,
we don't have an error down here anymore,
| | 05:18 | because we have a function called runTest.
| | 05:20 | In our runTest, I'm going to display a message,
and it is going to say Testbed version, and
| | 05:30 | I'm going to use our
kTestbedVersion variable there.
| | 05:34 | So, the message function actually is
variadic function, it works just like printf does.
| | 05:40 | You'll notice here in the
code it actually calls format.
| | 05:43 | So, it's a variadic function, it has va_list
and va_start just like a normal variadic function
| | 05:49 | in C and feel free to check out my C and C++ Essential
Training for tutorial on how variadic functions work.
| | 05:59 | And here, it uses NSString initWithFormat,
which is very much like format using C, and
| | 06:05 | it passes at the argument so that it
works like a normal variadic function.
| | 06:09 | So, that allows me to do things like
this %@ to display Objective-C object.
| | 06:17 | So now, we're going to display
a series of strings like this.
| | 06:23 | And this is using the new
Objective-C literal object syntax.
| | 06:32 | So, this is called a boxed array, and it's a much
easier way to declare an array than the old way.
| | 06:40 | So, I can just declare a bunch of strings
in here in this array like that, and then
| | 06:45 | inside in each of the
strings I can put some text.
| | 06:49 | So now we have our little runTest function.
| | 06:54 | It will display the TestbedVersion, and that
constant is declared in our .h file, and it
| | 07:02 | steps through this boxed array and
displays each of these strings individually.
| | 07:07 | So, when I save this, and I'm going to make sure
I've selected the iPhone 6.0 simulator and press Run.
| | 07:16 | It will go ahead and compile it, Build Succeeded, and it
will bring up the simulator, and there it is it's running.
| | 07:24 | And you'll notice that these messages
are now displaying in our text view.
| | 07:29 | So now, we have a working iOS testbed.
I find this a very valuable tool.
| | 07:35 | Whenever I have a specific task to accomplish,
I'll start with the testbed like this one,
| | 07:39 | and I'll do some prototyping to try out
different approaches and decide on a strategy.
| | 07:44 | This saves me a lot of time and headache
that I would otherwise spend trying to debug a
| | 07:48 | new idea while it's
intermingled with other working code.
| | 07:52 | We'll see an example of how
to use this in the next movie.
| | Collapse this transcript |
| Using the testbed| 00:00 | So now, I'd like to show you an example of
how I've used the Testbed in this application,
| | 00:05 | so you can get an idea
of how to use it yourself.
| | 00:08 | As I mentioned before, when I have a specific
task to accomplish, I'll start with the Testbed
| | 00:13 | like this one and do some prototyping
to figure out what approach to take.
| | 00:17 | This is an example of a task that I needed
to play with in order to understand it well
| | 00:21 | enough. So, let's open Testbed-dates.
| | 00:25 | The project is in the Xcode project file,
and I'll double-click on that, and that will
| | 00:30 | open it up in Xcode.
| | 00:32 | And if we open this up to the source code
here, you'll notice in TestbedViewController
| | 00:39 | everything else is the same.
| | 00:40 | I'm just going to get rid of that so
that we have a little room for the code.
| | 00:45 | In this runTest function, you'll notice
that I have a bunch of date testing codes.
| | 00:50 | So, I have Testbed
version and then date testing.
| | 00:53 | And I've got a for loop
with a boxed array of strings.
| | 00:58 | And then for each of those strings I'm converting
the string to a date format and then I'm displaying
| | 01:04 | that in a number of different date formats.
So, let's Run this in the iPhone simulator.
| | 01:11 | You'll notice that here are each of those
dates and in each of these different formats.
| | 01:16 | And so, I'm able to play with my approach
for how it is that I want to accomplish this
| | 01:20 | task and see the results in
real time here on the simulator screen.
| | 01:26 | So like I've said, the Testbed is a
useful tool, it's not an essential tool.
| | 01:29 | You can use NSLong for the same purpose.
| | 01:31 | But it is useful, and I use it a lot in my iOS development
work, and we'll be using it later in this course.
| | 01:37 | I hope you find it a useful
tool for your projects as well.
| | Collapse this transcript |
|
|
2. Building a Database LibraryUnderstanding SQLite in iOS| 00:01 | SQLite is a full featured relational database.
| | 00:04 | It's small, it's fast, it's lightweight, and
it's perfectly suited for mobile applications.
| | 00:09 | The SQLite Database Management
System lives entirely in a driver.
| | 00:15 | This means that there is
no server and no client.
| | 00:17 | You access the database directly.
| | 00:20 | The database itself is
fully contained in one file.
| | 00:24 | This makes it very convenient
to use in a mobile environment.
| | 00:28 | To say that iOS supports SQLite natively is to
say that the driver is built-in, the interface
| | 00:35 | to the driver is written in C, so it's not Object
Oriented, and it's not an Objective-C Interface.
| | 00:42 | To make it easier to use, we will build a Native
Object Oriented Interface for SQLite in Objective-C.
| | 00:49 | This kind of interface is sometimes called a
Wrapper as it wraps one interface around another.
| | 00:55 | It's designed to be convenient and
easy for our development purposes.
| | 00:59 | It's general purpose, so you can
use it in your future projects.
| | 01:03 | You have the source code, and you may
modify it and expand it to suite your needs.
| | 01:08 | To further support the iOS Model-View-Controller
architecture, we will build a more specific
| | 01:12 | interface on top of our general interface.
| | 01:15 | We will do this by subclassing our general
interface and adding methods to specifically
| | 01:20 | support this application.
| | 01:22 | This makes it very easy for us to
integrate the database into our application.
| | 01:27 | Subclassing a normalized interface like this is a
common and valuable technique in an object-oriented environment.
| | 01:33 | As you follow along with the development
process in this chapter, think about how you will
| | 01:37 | use and expand on these
techniques in your own projects.
| | Collapse this transcript |
| Creating an Objective-C interface for SQLite| 00:00 | The native SQLite interface is written in C,
and while it works very well, it's not very
| | 00:05 | convenient in an Objective-C environment.
| | 00:08 | So, we will begin by writing a wrapper,
which is sometimes called an Adapter Pattern, to
| | 00:13 | encapsulate the C Language SQLite interface
in a native Objective-C interface that's easy
| | 00:19 | to use and to extend for our purposes.
| | 00:22 | So first, let's take a look at what
we want this interface to accomplish.
| | 00:26 | I'm just going to open this BWDB-testbed-code.txt,
in my text editor, I'm using TextWrangler here.
| | 00:33 | You can use whatever editor you like just make sure
it's a real plain text editor and not a Word Processor.
| | 00:39 | So, you'll notice here is
the testDatabase function.
| | 00:46 | And this is the method that I'm going to be
using to test this database interface, and
| | 00:50 | so it shows how the interface is called.
| | 00:53 | And really my goal was to have
an interface that works like this.
| | 00:56 | So for instance, I can have something, that you know,
sends a message to the db object that says doQuery
| | 01:02 | and in a string it has an SQL query "drop
table if it exists", CREATE TABLE with this
| | 01:09 | schema, insert into table, values this
and that, and you notice that there are place
| | 01:13 | markers and then there are various values.
| | 01:17 | These are actually Objective-C
objects that are used as arguments.
| | 01:22 | And so, my interface has to support what we
call variadic arguments in C-based languages.
| | 01:29 | And so, with variadic arguments I can have
as many, or as few, of these arguments as I
| | 01:34 | like after the string that has the format
in it, and specifies how many arguments I'm
| | 01:41 | going to actually be needing.
| | 01:44 | My interface also allows me to do
something like this for row in and then I have this
| | 01:49 | expression here that calls this method
getQuery on the db object and has a query in it.
| | 01:56 | And so, what that will do is it will
actually go through the database and get one row at
| | 02:01 | a time and pass those rows off into this
row variable and allow me to operate on that.
| | 02:06 | It's a very convenient syntax for calling,
and it's accomplished in Objective-C with
| | 02:11 | a protocol called NSFastEnumeration,
and we'll see how that's done as well.
| | 02:16 | We also have these CRUD-based methods where I can do
something like insertRow and then have a dictionary.
| | 02:23 | This is a literal dictionary using
the new Objective-C literal syntax.
| | 02:28 | And I can just pass it a dictionary object,
and it will insert that row into a table that
| | 02:35 | has already been specified in the constructor.
| | 02:39 | So, I have CRUD insert, here is a
CRUD update and here is a CRUD delete.
| | 02:46 | So, that's how we want to be able to use this.
| | 02:49 | Let's go ahead and create an
interface that will do exactly this.
| | 02:53 | We're going to start by navigating to
the Chapter02 folder in the exercise files.
| | 02:57 | And we're going to make a
working copy of Testbed.
| | 03:01 | I'm just going to press Command+C on this
Mac and select my folder and press Command+V,
| | 03:07 | and that will allow me to
make a working copy here.
| | 03:09 | And I'm going to call this Testbed-02-working,
and I'm going to open it up by navigating
| | 03:15 | to the Xcode project file inside
of that folder and double-clicking.
| | 03:19 | So, this is actually our finished
Testbed from the previous chapter.
| | 03:24 | You notice if I select the iPhone
Simulator and press Run, it will build, and it will
| | 03:30 | run it in the iPhone Simulator.
| | 03:34 | And you can see this is exactly the same
Testbed that we had in the last chapter.
| | 03:38 | So, I'm going to come back over here to Xcode,
I'm going to press Command+Period
| | 03:42 | to stop these simulators, here it
says Finished running the simulator.
| | 03:46 | Now, we're going to select the target, so we select
Testbed up here in the top of the Project navigator.
| | 03:54 | And then, I will select Target over here and
Summary over here, and scroll down for a while,
| | 04:00 | and you see we have these Linked Frameworks.
| | 04:02 | We're going to add a
framework for our SQLite here.
| | 04:04 | So, I'm going to press the plus button, and I'm going
to type, sqlite in the Search there, sql was enough.
| | 04:12 | I'm going to select sqlite 3.
dynamic library and click Add.
| | 04:17 | And now, you'll notice that there it is in
our frameworks, and we've added the sqlite.dylib
| | 04:21 | to our Link Frameworks.
| | 04:25 | And then down here in our Testbed files,
we want to add our database library.
| | 04:31 | So, I've got a library
called BWDB in the exercise files.
| | 04:34 | So, we come back out here in the exercise files
and select Libraries, and just grab the BWDB folder.
| | 04:41 | You'll notice inside that folder there are
just a couple of files: a .m file
| | 04:44 | and a .h file. We'll take a
look at those in a little bit here.
| | 04:47 | So, we're going to grab this whole folder,
and we're going to bring it over here until
| | 04:52 | Testbed is highlighted.
| | 04:53 | You see, you can highlight different things
here, and you want to highlight Testbed in
| | 04:58 | the folder version of it there, and it
will then drop that in right there.
| | 05:03 | Brings up this dialog box, you want to select
Copy items, Create groups, and Add to targets
| | 05:09 | for Testbed, and Finish.
| | 05:11 | And so, it copies those files, it creates
a group for them called BWDB, and there are
| | 05:16 | the files, there's the m file which is the
implementation and the h file which is the header file.
| | 05:21 | Let's just take a quick look here.
| | 05:24 | The header file has our interface, you notice
it I reads in the sqlite3.h header file from
| | 05:30 | the SQLite 3 Framework that we imported earlier.
| | 05:33 | And you'll notice down here,
we have our actual interface.
| | 05:39 | We have a constructor with Filename, a
constructor with a Filename and a TableName that's used
| | 05:44 | for the CRUD methods.
| | 05:46 | We have an openDB, closeDB, a d structure--
dealloc is a d structure--and it basically
| | 05:51 | just closes the database.
We have Fast enumeration support.
| | 05:55 | That allows us to do that trick where we
put the object in an enumeration for loop.
| | 06:00 | We have SQL queries, doQuery,
getQuery, prepareQuery, valueFromQuery.
| | 06:05 | And you notice these
have the variadic arguments.
| | 06:08 | If you want to know more about variadic
arguments in C-based languages, you can take a look
| | 06:12 | at my C and C++ Essential Training on
the lynda.com online training library.
| | 06:19 | These raw results are pretty much just used
internally to translate these variadic queries
| | 06:25 | into something that will return a value.
And then, here we have the CRUD methods.
| | 06:30 | This is the heart and soul of this.
| | 06:31 | We can insert a row, update a row,
delete a row, get a row, and count rows.
| | 06:37 | And we also have Subscripting methods that
support the new keyed subscripts that are
| | 06:41 | available in the latest version of Objective-C.
| | 06:45 | This is all implemented in our .m file and
just briefly we're not going to go through
| | 06:49 | this in a lot of detail.
| | 06:51 | You've got the source code here, and you're
welcome to look through it and modify it and
| | 06:54 | use it in whatever way you like.
| | 06:57 | Our constructors simply set up the
file name and open the database.
| | 07:01 | And they call this openDB, which goes through
a little bit of trouble to actually find the
| | 07:07 | database file in the iPhone iOS file system.
| | 07:11 | An SQLite database is kept entirely in one
flat file, all the tables in a given database
| | 07:18 | are all in one very portable file, which is
why SQLite is used on so many mobile platforms.
| | 07:26 | closeDB simply calls SQLite 3 to close, that's the C
interface for SQLite 3 and dealloc simply calls closeDB.
| | 07:35 | Here is the Fast enumeration support.
| | 07:37 | There's very little documentation in the Apple
documentation of Objective-C as to how fast enumeration works.
| | 07:45 | Basically they give you some examples, and
you can play around with those examples until
| | 07:48 | you get something to work,
which is what I did here.
| | 07:53 | Here is our basic doQuery, which you'll
notice uses variadic arguments here and here is the
| | 07:59 | va_list and va_start for the variadic arguments.
| | 08:02 | And you'll notice that it uses the SQLite C interface,
sqlite3_step, sqlite3_finalize, sqlite3_changes.
| | 08:09 | And again, prepareQuery is very simple,
getQuery is likewise simple, as is valueFromQuery.
| | 08:19 | bindSQL is where a lot of the work is done.
| | 08:23 | This actually introspects the arguments, the
objects that are passed in the variadic list
| | 08:30 | using respondsToSelector to figure out what
the type is of the argument and to call the
| | 08:37 | appropriate sqlite3_bind_int, double sqlite3_
bind_text in order to interface with SQLite.
| | 08:48 | Our raw results here are used
mostly internally within the BWDB class.
| | 08:53 | And these are our CRUD methods,
this is for inserting a row.
| | 08:57 | InsertRow uses some string methods in order to
construct a sql query to insert the row into the database.
| | 09:06 | Likewise, updateRow constructs an sql
query for updating a row in the table.
| | 09:13 | deleteRow and getRow are very straightforward,
because how you do that in SQL is very straightforward.
| | 09:23 | And likewise, these Keyed
Subscripting Methods are also very simple.
| | 09:28 | And then we have a couple of simple Utility Methods
for getting a call in value in getting the lastInsertId.
| | 09:33 | So, you can see that the value of the
wrapper is in its ability to hide the complexity of
| | 09:39 | the native SQLite interface from
the Objective-C implementation.
| | 09:43 | The C Language interface for SQLite
is full-featured, but it's cumbersome.
| | 09:48 | And when we finish the Testbed
implementation using this wrapper, we'll get a good idea
| | 09:52 | of just how much easier it
is to use this in Objective-C.
| | Collapse this transcript |
| Testing the BWDB interface in the sandbox| 00:00 | At this point we have started to
incorporate BWDB in the Testbed code for testing.
| | 00:06 | We've set up the SQLite 3 Framework in Xcode, and
we've imported the BWDB Library into our Testbed app.
| | 00:13 | Now, it's time to test
the library in the testbed.
| | 00:16 | So, you're going to take the results from
your last exercise or if you prefer, you can
| | 00:22 | use Testbed-02-done, which is my version.
And I'm going to make a working copy of this.
| | 00:28 | And I'm just going to call it Testbed-03-working,
and so this way we can pick up where we left
| | 00:34 | off at the end of the last movie.
| | 00:36 | And I'm going to double-click on the Testbed.
xcodeproject inside of that folder to open the project in Xcode.
| | 00:44 | So, we have our BWDB Library, there
is the m file, and there is the h file.
| | 00:48 | I'm just going to close that up
for now, we go to our BWUtilities.
| | 00:53 | And I'm going to come over here to the
TestbedViewController.m file, and up here at the top I'm going to
| | 01:01 | import the BWDB header.
| | 01:04 | And I'm going to come back out here into our
finder, and I'm going to take this BWDB testbed
| | 01:11 | code and open that up in my text editor.
And we've got a couple of methods here.
| | 01:17 | I'm just going to copy these
and paste them into my m file.
| | 01:21 | So, Command+C to copy, coming back here
into Xcode, and down here in the implementation
| | 01:29 | I'm going to paste those in, and there they are.
| | 01:33 | And you see that our method
here is called testDatabase.
| | 01:37 | And so, I'm just going to come down here in
viewDidLoad and instead of runTest, I'm just
| | 01:43 | going to type in testDatabase like that.
| | 01:48 | And so runTest here is actually often we can
leave it there, or you can delete it, it's up to you.
| | 01:54 | So, let's just go ahead here and run this and
then we'll come back, and we'll look at the code.
| | 01:59 | Make sure you select the iPhone Simulator
and press the Run button, Build Succeeded,
| | 02:05 | that's always a good sign.
| | 02:08 | And so there is the Simulator, we see we have
our BWDB version, SQLite version, create table,
| | 02:15 | and all of these database
operations happening successfully.
| | 02:20 | So, let's take to look at the code.
So, there's a table name and a file name.
| | 02:26 | And there is an actual
BWDB object being declared.
| | 02:30 | And here we have our alloc init pattern, and we're
initializing with the file name and the table name.
| | 02:39 | And we call getVersion to display the
version, and there is our version there.
| | 02:44 | And SQLite version is value from query
select sqlite_version, so there is the sql query
| | 02:50 | to get the SQLite version, and
that's getting displayed here.
| | 02:54 | So, that actually tells us that
a lot of stuff is working here.
| | 02:59 | Our interface to the SQLite Framework is
working and the interface to BWDB is working.
| | 03:09 | So, we're actually able to send an SQL query and
get a value from that, and that's a really good sign.
| | 03:15 | So now, we come out here, and we create a table.
| | 03:17 | You notice I have this message call to
create--to display the create table string.
| | 03:23 | And doQuery is the version of the query code,
and if we come in here to the BWDB header
| | 03:31 | and SQL query's doQuery simply returns a
number of rows that were affected, if any.
| | 03:38 | So, it's typically called
without testing the return value.
| | 03:42 | And we come back down here, and we can see doQuery
here has been called without testing the return value.
| | 03:47 | Drop table if exists and then CREATE TABLE.
| | 03:52 | We're creating a table with an id column,
INTEGER PRIMARY KEY that's SQLite for an auto
| | 03:59 | incremented integer key.
| | 04:02 | SQLite allows you to create columns
without a type because it's really a very loosely
| | 04:08 | typed thing anyway.
| | 04:10 | And stamp is TEXT DEFAULT CURRENT_TIMESTAMP and so
it will always put a timestamp on every single row.
| | 04:18 | And so, now we're going to go ahead and
insert a row, and we insert into BW table, so we're
| | 04:23 | using doQuery for this insert, and we return the
lastInsertId, and we see that we get one for that.
| | 04:33 | And then using CRUD to insert with insertRow,
and these are literal NSDictionary types.
| | 04:41 | So, we have key value, key value, key value,
that's a new feature in Objective-C, and it's
| | 04:48 | much more convenient than the old way.
So, I tend to use these literal values a lot.
| | 04:53 | So, you see I'm using some different types
here, I've got the literal floating point,
| | 04:58 | I've got literal integers
and here is a literal string.
| | 05:02 | So, NSNumber doubles, integers and Mixed.
| | 05:06 | And you see, when we look at these values,
we get the return values based on the way
| | 05:11 | that SQLite handles these sorts of things.
| | 05:15 | And here I'm reading it back, and that's where
it's displayed here using the NSFastEnumeration.
| | 05:22 | And so, I simply pass the query into the
db object and for a row in, the value that's
| | 05:28 | returned by that is the actual db object.
| | 05:30 | If we look at the getQuery code over
here, we notice that it returns itself.
| | 05:37 | So, that's actually returning the BWDB object.
| | 05:42 | And because of NSFastEnumeration, I'm able
to just say for row in that object, and then
| | 05:48 | call display row with that row.
| | 05:51 | And display row of course is up here, and
it's just this simple message with the row.
| | 05:56 | And again, we're using this new
subscripting feature that's new in Objective-C.
| | 06:01 | So, we use db countRows here, and there we
have count rows 4, and then we delete a row
| | 06:09 | and then we get count rows 3.
| | 06:11 | So here, we delete row three with deleteRow, and
we call countRows again we get a different value.
| | 06:17 | And then we look, and we have just row 1,
row 2 and row 4, and that again is using the
| | 06:22 | NS enumeration for
displaying the rows in the table.
| | 06:26 | And then we're able to display
an individual row using getRow.
| | 06:30 | So, this should make it clear that the value
of this wrapper is it makes it easy to work
| | 06:36 | with SQLite in an Objective-C environment.
| | 06:39 | We are using Objective-C paradigms for all
of this, rather than having to write C code
| | 06:44 | in the middle of our Objective-C.
| | 06:45 | It makes this all seamlessly
integrate with our Objective-C code for iOS.
| | 06:52 | It's also easy as we'll see in another
lesson in this chapter to subclass BWDB and create
| | 06:57 | a specific interface for a specific application.
| | Collapse this transcript |
| Designing a database schema| 00:00 | Because we're using a database for this
application, we must start with a database schema.
| | 00:04 | A schema is simply a description of the
structure of the database that contains descriptions
| | 00:10 | of tables, columns, indexes, and other
associated constructs that are part of the database.
| | 00:16 | There are tools available for
defining a schema for a database.
| | 00:20 | But this database is simple, and we'll be
writing it in SQL in a text editor, and creating
| | 00:24 | the database using the
SQLite 3 Command Line client.
| | 00:28 | So, in your Chapter 2 folder in exercise files,
you'll find a file called bwrss.sql, and I'm
| | 00:36 | going to open that in my text editor.
| | 00:37 | I'm using TextWrangler which is
free, and it is a great text editor.
| | 00:42 | You may use whatever text editor you like
just make sure it is not a word processor.
| | 00:47 | This is a very simple database.
| | 00:50 | It has two tables, one
for Feeds and one for Items.
| | 00:53 | You'll notice here, at the top, we do DROP
TABLE IF EXISTS feed, and DROP TABLE IF EXISTS item.
| | 00:59 | This is SQL, and if you need a premier on
SQL, I have a course on the lynda.com online
| | 01:06 | training library called SQL Essential
Training, which would help you in SQL.
| | 01:10 | There is also one specifically for SQLite
called SQLite Essential Training with PHP,
| | 01:16 | and that will give you a really good
introduction to the SQLite Database engine.
| | 01:20 | So we drop the tables if they
exist, and then we create the tables.
| | 01:23 | And each of these tables again is very simple,
it has a unique ID for the record and they
| | 01:28 | both have that URL for the data, title, description, the
date of the last time it was updated, and a timestamp.
| | 01:37 | For the Item, it is the same thing.
| | 01:38 | There is a feed id which links back to the
ID in the feed table that makes these two
| | 01:44 | tables related to each other, and there's the URL of
the item, title, description, publication date, and stamp.
| | 01:50 | They're really very simple tables.
| | 01:54 | We create a UNIQUE INDEX for the feed so that
we look them up by URL, which we actually do.
| | 02:00 | And then we insert into the database
Default feeds, one for the lynda.blog, one for the
| | 02:05 | lynda.com New Releases, and one for my
Technology Blog, which I update about once a year.
| | 02:11 | So, that's the SQL for creating the database.
It is really very simple.
| | 02:16 | We call this a database schema because it
describes the database itself, and it is also
| | 02:23 | SQL so we can use it to
actually create the database.
| | 02:26 | We are going to do that right now.
| | 02:27 | I am going to quit my text editor, and
I'm going to open up a terminal session.
| | 02:32 | This is a terminal session on a Mac.
| | 02:33 | It is basically a Unix Command Line, so
I am going to be typing Unix commands.
| | 02:39 | The Unix command cd stands for Change
Directory, that allows me to navigate to a particular
| | 02:45 | location as if I was
clicking around in the Finder.
| | 02:48 | So I am going to say cd Desktop/, and you'll
notice that I just typed a couple of letters,
| | 02:54 | and I pressed the Tab key, because this is
the bash shell on Unix works like that and
| | 03:00 | so it has something called Command Completion.
| | 03:01 | I can just type a few letters and press
Tab, and it will complete things for me.
| | 03:05 | And so it puts in that slash for the subdirectory,
and I can type E-X-E and press Tab, and it will
| | 03:11 | get my exercise files on the Desktop,
which is what's right over here.
| | 03:15 | And then I'm going to type capital C-H-A--
these are case-sensitive, so I'm actually typing
| | 03:21 | the capital letters--and press Tab, and it
takes me to Chap0 because there are several
| | 03:26 | of them if I press Tab again
you'll see I'll get a choice.
| | 03:29 | So I can press the 2 and Tab again.
| | 03:31 | It gets me to my Chapter 2 folder press Return,
and there I am, if I type Pwd it will tell
| | 03:36 | me where I am in the File System.
| | 03:41 | L-S will give me a listing of the folder or
directory, and you notice it's got the same files here.
| | 03:46 | At this point what I want to do is I want
to take this SQL File, which is this one here
| | 03:52 | that we're just looking at in our editor.
| | 03:54 | I want to read that into the SQLite 3 Command
Line Utility as a script to create a database.
| | 04:02 | So the way I do that is I type sqlite3,
which is the name of the Command Line Utility for
| | 04:08 | SQLite 3, and it comes with
your Mac. It is built-in.
| | 04:11 | I am going to give it the name of the
database, I'm going to call it bwrss.db.
| | 04:16 | You'll notice that we don't have that yet.
It is not listed here in the files.
| | 04:20 | It is not listed here in the files.
| | 04:22 | There is a done version of it, which is
exactly what we are going to be creating.
| | 04:28 | Then I used the left angle bracket, which
is a Pipe Command in Unix shell, which means
| | 04:34 | to read-in this file I'm about to
name and use that as if I were typing it.
| | 04:40 | The file I am going to read-in is bwrss.sql.
| | 04:44 | This command here sqlite3 bwrss.db
the left angle bracket, and bwrss.sql.
| | 04:54 | That will read-in this
sql file into this command.
| | 04:58 | This command here is sqlite3 bwrss.db that
will that will run the SQLite 3 Utility, it
| | 05:04 | create this database file if it does not already
exist and then read-in these commands and exit.
| | 05:09 | I am going to press Enter, and you
notice this doesn't take very long at all.
| | 05:14 | If you saw over here, we
now have this file bwrss.db.
| | 05:19 | If I type L-S here, you notice we now have
that file here we did not have before bwrss.db.
| | 05:25 | If I type L-S dash L bwrss.db, it will give us
some statistics about this file, and you notice
| | 05:33 | that it's 16,384 bytes in size.
| | 05:37 | In fact, if I say ls-l bwrss-done.
db, it is exactly the same size.
| | 05:46 | That is the one that I created earlier.
| | 05:49 | The thing about SQLite is that the entire
database is stored in this one file, unlike
| | 05:55 | other database management systems where
they have various files for indexes and for the
| | 05:59 | different tables and sometimes even for columns.
| | 06:02 | Everything is contained within this one file
in SQLite 3, which is part of the beauty of it.
| | 06:07 | It is in fact a platform agnostic file,
which means that I can take this same file, and
| | 06:12 | I can use it in Unix, I can use it in Windows,
I can use it in any other platform, and be
| | 06:17 | able to actually read it and write to it
and use it with any SQLite 3 application.
| | 06:22 | Right now, I'm just going to use this Command
Line Utility, sqlite3 bwrss.db, press Enter.
| | 06:31 | I am going type in an sql
statement, select * from sqlite_master.
| | 06:41 | Semicolon terminates a command in SQL.
| | 06:45 | I'm going to press Enter, and it
describes what's in this database.
| | 06:50 | So I have these two tables.
| | 06:52 | There is the actual SQL
that was used to create them.
| | 06:56 | I have this unique index.
Also, I can look at the feed table.
| | 07:02 | I can say select * from feed and a Semicolon, and
there is the data inside that feed table.
| | 07:09 | That's the data that we inserted using the SQL.
| | 07:12 | If I come over here and open that up again,
and you see down here at the bottom, we inserted
| | 07:17 | these rows into the table.
There we have them in our Command Line Utility.
| | 07:23 | So I type .quit, and that
will exit the SQLite 3 Command Line Utility.
| | 07:31 | Type exit here at Shell Prompt,
and I can Quit Terminal now.
| | 07:38 | I can quit TextWrangler. There we have it.
| | 07:41 | We have created our database, and
there it is in our bwrss.db file.
| | 07:47 | That's the complete working SQLite 3 database.
| | 07:49 | It is really all there is to it, the
entire database is contained in this one file.
| | 07:53 | The file was completely cross-platform, so
no matter what operating system you use to
| | 07:58 | create it, it will work
wherever you want to use it.
| | 08:01 | All we need to do now is copy this file into the App
and use it, and we'll do that in the next movie.
| | Collapse this transcript |
| Supporting the application with a specific interface| 00:00 | At this point, we have a working SQLite 3
database wrapper class called BWDB and a working
| | 00:06 | SQLite 3 database called bwrss.db.
| | 00:10 | Now we want to take these components and make an
Objective-C class for using these components in iOS.
| | 00:15 | So we will start by making a
working copy of our project so far.
| | 00:20 | So you can make a working copy of your
results from movie three or like I am going to do
| | 00:24 | here, I am just going to make a working copy
from Testbed-03-done, and I am going to rename
| | 00:31 | this to Testbed-05-working.
| | 00:36 | I am going to open up the project here in
Xcode by double-clicking on the Xcode project file.
| | 00:43 | Here we've got our BWDB Library, and you notice
our testdatabase method here in our ViewController.
| | 00:49 | I'm going to come right back over here to
the Finder, and I am going to--first of
| | 00:54 | all, I'm going to read-in our database.
| | 00:56 | This is the database we
created in the last movie, bwrss.db.
| | 00:59 | I'm just going to drag this over here into our
target, and you notice that I get this dialog box.
| | 01:07 | I want to make sure Copy is checked, and I
also want to make sure Add to Target is checked
| | 01:10 | for our Testbed target.
| | 01:13 | I'll click Finish, and there's the file
there, right next to the dynamic library.
| | 01:16 | And now I'm going to come back out here in the Finder,
I am going to Libraries, and I am going to do the same thing.
| | 01:21 | I am going to drag this RSSDB folder, and
we'll take a look at this one as we import it.
| | 01:26 | This is going to go in our
Testbed group here inside of the target.
| | 01:30 | I'll let go of that and make sure that Copy
and Create Groups and Testbed are all selected,
| | 01:38 | and there is our RSSDB code.
| | 01:40 | If we look up here at the Header File, we
see that what we are doing is we're extending
| | 01:45 | the BWDB class, with a new class called RSSDB.
This new class has got a bunch of methods in it.
| | 01:55 | This is all specific for
dealing with feeds and items.
| | 01:58 | If we come over here into the m file, the
implementation file, you see we've got a new
| | 02:04 | constructor that calls the super constructor.
| | 02:07 | That's the constructor of the parent class,
the inherited class, and it initializes a
| | 02:13 | list of id's, and it sets some defaults.
| | 02:17 | So it is basically extending it with
file name constructor from the parent class.
| | 02:23 | getVersion is just over written.
| | 02:25 | This getMaxItemsPerFeed, that's reading a preference
pane value, we'll get to that later on in the course.
| | 02:32 | This addNewIndex, there was a previous
version of this App, which used a database file that
| | 02:38 | did not have an index, and so it just
creates the index if it doesn't exist.
| | 02:42 | You'll notice that it is just using SQL for that,
and it's calling doQuery on the parent class.
| | 02:48 | RSSDB because it inherits BWDB,
everything in BWDB is available.
| | 02:55 | As a result of that, all of these methods
that have to do specifically with feeds and
| | 03:01 | with items, these are all very, very simple
and straightforward some of them have some
| | 03:06 | SQL in them but they are just
calling doQuery from the parent class.
| | 03:10 | So it makes it really easy because we
already have this wrapper, it makes it really easy
| | 03:15 | to write these functions that are very
specific for this application and to simply put them
| | 03:20 | in a class extension.
| | 03:22 | Some of the most complicated stuff like
this deleteOldItems is all accomplished in SQL.
| | 03:29 | So you can see that it makes this job a lot
easier that we can just extend a class that's
| | 03:33 | already written, that's already wrapping the
SQLite C interface in a nice Objective-C interface.
| | 03:40 | Now, we can come out here
to our TestbedViewController.
| | 03:45 | Instead of BWDB here, we
are going to import RSSDB.h.
| | 03:53 | Now, we are going to come down here in our
dispRow and our Test database.
| | 03:57 | We are just going to replace those.
| | 04:00 | So I'm going to select all of that, and I'm
going to come back out here to the Finder,
| | 04:03 | and in Chapter 02, RSSDB-testbed-code.txt,
I'm just going to select all of that and copy
| | 04:11 | it and come back over here
into Xcode and paste that in.
| | 04:16 | You can see that's very simple, so
I am pressing Command+S to save.
| | 04:22 | You see all this does is it
initializes this RSSDB object.
| | 04:27 | It passes it the file name bwrss.db, that's
the database that we created in the last lesson,
| | 04:35 | and we've already copied it into our project.
| | 04:38 | It just displays the RSSDB version, and
it reads these feed id's and displays them.
| | 04:47 | Now we are going to make sure that the
iPhone Simulator is selected and go ahead and run
| | 04:52 | this in the Simulator.
There we have our results.
| | 04:56 | It is possible that you may get just the
version number and not the actual database.
| | 05:01 | There may be some errors down here on the
screen. If that's the case, what you need to do is
| | 05:05 | you need to actually delete the App
from the Simulator and run it again.
| | 05:10 | The reason for this is if this App has been
loaded before, and it did not have the database
| | 05:15 | in it, and then you load the database in Xcode
and run it in the Simulator, it may not actually
| | 05:22 | replace the whole bundle in the Simulator.
| | 05:24 | So you have to delete the
bundle and load it again.
| | 05:28 | It sounds complicated, but if you
just follow these steps, it should work.
| | 05:32 | The first thing I am going to do is I am going to
select Xcode, press Command+Dot, and that
| | 05:37 | will bring up this message
Finished running Testbed on the Simulator.
| | 05:40 | Let's switch back to the Simulator, I am
going to click on Testbed here and hold it down,
| | 05:46 | you notice that it does this little wobbly thing just
like on your iPhone when you go to delete something.
| | 05:52 | I am going to press the Delete, and I am
going to actually delete that whole bundle.
| | 05:57 | Because my Simulator is the small version
because I have the small screen, there is
| | 06:00 | no button, I happen to know that
Command+Shift+H presses the Home button.
| | 06:06 | You see that the whole second page is gone
now, because there is no App that goes there.
| | 06:10 | You may have other Apps there, and that's fine.
You just want to make sure deleting this one.
| | 06:14 | Now I come back over here to Xcode.
| | 06:16 | I have deleted the bundle off of the Simulator,
and when I run it again, it is going to load
| | 06:20 | up the correct bundle.
You see that now we have the database.
| | 06:25 | If you get that condition where it just shows
the RSSDB version number, and it doesn't show
| | 06:29 | any of the data that is probably because you've
got an old version of the bundle on your Simulator
| | 06:34 | that didn't have the database in it.
| | 06:35 | You just delete that bundle, run it again
from Xcode, and it should load up the correct
| | 06:40 | bundle with the database in it.
| | 06:41 | Here, we are just displaying these rows from
the database, and we're showing that our App
| | 06:46 | Specific Interface is working.
| | 06:48 | As we go through the rest of the
application the rest of this course, we will be using
| | 06:51 | and discussing more of the
methods from this library.
| | 06:54 | An App Specific Database Interface is a
simple technique, and it is a powerful tool.
| | 06:59 | As your apps get larger, this
technique becomes more and more valuable.
| | 07:02 | I find that it is almost
always worth the effort.
| | Collapse this transcript |
| Using C pointers with automatic reference counting (ARC)| 00:00 | At the Worldwide Developers Conference in 2011,
Apple introduced an extension to Objective-C
| | 00:06 | called Automatic Reference Counting, or ARC.
| | 00:09 | Like many object-oriented languages, Objective-C
uses the concept of object ownership to keep
| | 00:14 | track of how many places an object is used
so that it may know when to retain and release
| | 00:19 | memory associated with objects.
| | 00:22 | This was traditionally handled manually by
the programmer using retain and release method
| | 00:26 | calls on the globally-inherited NSObject class.
It was messy, but it worked.
| | 00:31 | The new Automatic Reference Counting
feature, or ARC, automates this process.
| | 00:36 | ARC is a pre-compilation phase that
automatically creates all of your retain and release calls,
| | 00:42 | so you no longer have to do that manually.
| | 00:45 | This is a fantastic addition to Objective-C, and
it makes your job as a programmer a lot easier.
| | 00:50 | Unfortunately, it also creates some new problems,
one of which is how to interface with libraries
| | 00:55 | written in plain old C.
Let's take a look at an example.
| | 00:59 | Here we have BWDB.h, which is a wrapper,
around a C library for the SQLite 3 data base.
| | 01:08 | And originally I had written this wrapper
before ARC was introduced, and it worked just
| | 01:13 | fine, and when ARC was introduced, it gave
me trouble around this bit of code right here,
| | 01:19 | which is the Fast Enumeration interface.
| | 01:24 | And the reason for this is that we're passing
it plain old C pointers like this stack buffer
| | 01:31 | and ARC was having trouble dealing with that.
| | 01:34 | Now, SQLite is of course, widely used for
persistent storage throughout iOS and Fast
| | 01:39 | Enumeration is a feature that's not actually
very commonly used but is very, very handy.
| | 01:45 | SQLite is of course, written in plain old C,
and it has no concept of retain and release.
| | 01:50 | It just uses plain old malloc
and free for its memory allocation.
| | 01:54 | For the most part, this is not an issue,
and it's completely transparent, but when you
| | 01:57 | need the interface with the foundation
protocol like Fast Enumeration, it becomes an issue.
| | 02:03 | So, if we look over here in the .h
file, we see this NSDictionary enumRows, and
| | 02:10 | this is used right there, and right here in the
Fast Enumeration countByEnumeratingWithState method.
| | 02:20 | So, what I had to do is in order to make this work is
I had to declare this as unsafe, un-retained storage.
| | 02:28 | And this is just one of the four
storage classes that are available.
| | 02:32 | It's actually a new one that was introduced
with ARC and basically it just tells the compiler,
| | 02:38 | when you're doing your ARC stuff, ignore
this completely. And that works fine because
| | 02:43 | it's statically allocated, and it doesn't
need to be retained and released anyway.
| | 02:48 | The other thing that I had to do is I had to
qualify this id for stackbuf with unsafe_unretained.
| | 02:57 | And again, this was not in the example for
the Fast Enumeration on how to implement it
| | 03:03 | in your own class as it's
not very well-documented.
| | 03:05 | It's only documented by example and so
I kind of had to figure this out myself.
| | 03:10 | But in a nutshell unsafe_unretained tells
the system to bypass reference counting and
| | 03:15 | not to zero out pointers.
| | 03:17 | And this makes this
particular feature work with ARC.
| | 03:22 | Now, using Fast Enumeration is a
convenience, it's not really a requirement.
| | 03:26 | I find it very convenient
and powerful and useful.
| | 03:29 | It's also powerful and useful to understand how
ARC works, not just to use it but to understand
| | 03:34 | what it does and how to use it
especially in exceptional use cases like this one.
| | 03:39 | So, if you'd like to know more about ARC,
this page on the llvm website explains all
| | 03:45 | the gruesome details about how Automatic
Reference Counting is implemented in Objective-C.
| | 03:51 | It's a bit dense, but I find that
it was worth the effort to read it.
| | Collapse this transcript |
|
|
3. Creating the Table View AppUnderstanding the table view| 00:00 | Table view is one of the most
useful and revolutionary features in iOS.
| | 00:04 | I was at the Macworld keynote address in
2007 when Steve Jobs first announced the iPhone.
| | 00:10 | When he first put his finger on the list of
items and pushed it into a scroll, the audience
| | 00:15 | literally got up and cheered.
| | 00:17 | They had never seen a mobile interface
this intuitive, and this easy to use.
| | 00:22 | This is Apple's email app that comes with the iPhone,
it's a simple Table view, it's completely intuitive.
| | 00:27 | You see this view, and
you just want to scroll it.
| | 00:30 | Table view is incredibly flexible too.
| | 00:32 | Here is the settings app.
This represents a grouped table view.
| | 00:36 | The organization of items in
this view is clear and obvious.
| | 00:39 | The IMDB app uses a Table view
to display REACH information.
| | 00:44 | You got the movie poster and lots of date
in the first cell, ratings in another cell,
| | 00:48 | other artwork, an obvious link to the trailer.
| | 00:51 | Again, it's intuitive you need no training to
know that you pressed on that to watch a trailer.
| | 00:56 | Then scroll down to get more
information in other groups.
| | 00:59 | It's clear, and it's obvious.
| | 01:01 | This is the RSS app that
we're building in this course.
| | 01:04 | We're using a simple table view for
displaying and selecting from list of feeds and items.
| | 01:09 | As we work with it, you'll see how easy it
is to work with the table view interface.
| | 01:14 | The Table view is a powerful and flexible
interface that's useful for a variety of applications.
| | 01:19 | Take the time to
experiment with it. Play with it.
| | 01:22 | Learn what it has to offer.
| | 01:23 | The REACH interface provides a wealth of
options that you can advantage of to provide you data
| | 01:28 | and navigate with a clear and obvious,
user interface in your own applications.
| | Collapse this transcript |
| Creating the view controller| 00:00 | As the first step of creating our data
driven application, we will start a new project in
| | 00:05 | Xcode and create the main table view controller.
| | 00:09 | This is the project that will
become our BW RSS application.
| | 00:13 | So start by opening Xcode, and we're going
to create a new project, so File New, select
| | 00:20 | Project and under iOS and application we're
going to select the master detail application.
| | 00:28 | This gives us a table view in the iPhone version,
and it gives us a split view in the iPad version,
| | 00:36 | so we'll go ahead and press Next,
and we're going to name our project.
| | 00:39 | We're going to call it BWRSS, organization
name, you'll just use whatever you use for that.
| | 00:45 | Company identifier, or I'm using example.com,
and this becomes part of the bundle identifier,
| | 00:51 | as you can see down here.
| | 00:52 | It's in reverse domain order and so you
want to use your domain name, example.com is a
| | 00:58 | generic one that's set aside for
educational purposes like this.
| | 01:02 | And so that's the one that I'm using, and
you're welcome to use that or whatever you
| | 01:05 | like in reverse domain format.
| | 01:07 | Under class prefix, again, I'm going to use
BWRSS here, and it'll use this to start the
| | 01:13 | names of the classes.
| | 01:14 | I'm going to select universal, this will
create a universal application, an application that
| | 01:19 | has support for both iPhone and iPad.
| | 01:23 | For the first part of the course, we're just
going to be developing with the iPhone version
| | 01:27 | and then later on the course, we'll take
those same classes, and we'll adapt them for use
| | 01:31 | with the iPad, and you'll see how this is done.
| | 01:34 | I'm going to check Use Storyboards
and Use Automatic Reference Counting.
| | 01:39 | We're not using Core Data, we're not using
Unit Tests, and I'm going to press Next, and
| | 01:43 | it'll give me an opportunity to
create a local git repository.
| | 01:48 | We're not going to be using this, but it's
always a good idea and I'll refer you to the course
| | 01:51 | on git in the lynda.com online training library.
| | 01:56 | And so we'll select Chapter three, and I'm
going to click Create, and it has created
| | 02:01 | our project, and if I come over here back
to the finder you'll see that there's project
| | 02:05 | called BWRSS, and there's the Xcode
project file that's open up in Xcode.
| | 02:10 | At this point, we haven't done anything but
create the project and Xcode has assembled
| | 02:14 | a set of templates for us that create
actually a working table view application.
| | 02:19 | So if I go ahead and launch this in the
simulator, so I'm going to select the iPhone Simulator
| | 02:25 | and click Run, notice that it compiles it,
it builds it without any problems, and it
| | 02:31 | loads up the simulator, and there's
our little empty table view application.
| | 02:36 | So I'm going to select Xcode again,
I'm going to press Command
| | 02:39 | and the period, and that will stop the
application running in the simulator.
| | 02:44 | At this point, there's a couple of things
we want to do to do the interface before we
| | 02:47 | start implementing the database.
| | 02:49 | First, we're going to rename
the Master View Controller Class.
| | 02:53 | So you see we have this class here called
BWRSSMasterViewController, and I just want
| | 02:59 | to rename this to be FeedsTableViewController.
| | 03:05 | And so, I'm just going to select that and
copy it here because we'll be using this.
| | 03:10 | I'm going to come down here to where it says
interface, and it has this BWRSSMasterViewController,
| | 03:14 | and I'm going to right-click or Control-click
and come down to Refactor and Rename.
| | 03:21 | And there I'm just going to paste in what I
typed before BWRSSFeedsTableViewController,
| | 03:27 | and when I click preview it'll show me all
the changes that it's going to make here.
| | 03:31 | You'll notice it updates it in the storyboards,
it updates it in the .h and .m files,
| | 03:38 | and it shows me all of these places where
it actually changes it, and that's a useful
| | 03:43 | feature of Xcode, it's
refracturing for renaming.
| | 03:46 | I'm just going to say Save, and we'll get
this little dialog box here that asks if we
| | 03:50 | want to make snapshots.
| | 03:51 | I'm going to say Enable because
that never hurts. And there we go.
| | 03:54 | Now we see that these files are now named
FeedsTableViewController, I'm just going to
| | 03:58 | update also this one here,
this little comment here.
| | 04:03 | Save that, and I'm going to come over here
to the iPhone storyboard, and I'm just going
| | 04:08 | to make a little more real estate on my
screen here, and I'm going to come in up here where
| | 04:12 | it says Master, and I'm just going to change
that to say BW RSS, and we'll get back to the
| | 04:20 | storyboard later and do some more things.
| | 04:21 | For right now, I'm going to also delete
these files over here, BWRSSDetailViewController,
| | 04:28 | and I'm going to move it to the Trash, and
I'm going to come in here to our .h or
| | 04:33 | rather our .m file, and I'm going to
delete this detail view controller .h reference.
| | 04:41 | And so, we're just kind of cleaning this up
a little bit to get ourselves a nice starting
| | 04:44 | place for our application because I wanted to
reflect the type of application we're building
| | 04:51 | not just some generic application.
| | 04:54 | And then just a couple of more clean up
things we want to do here, our .h file should
| | 04:58 | be just fine, yeah, actually we need to
remove this reference to the detail view controller
| | 05:04 | and remove this reference to
the detail view controller.
| | 05:08 | And then in our .m file,
there's a couple more things to do here.
| | 05:12 | Up here this little selector at the top
allows me to jump around in the file and come down
| | 05:17 | here to didSelectRowAtIndexPath, and
I'm just going to comment all of this out.
| | 05:24 | We'll get back to that later and likewise here
this prepareForSegue, comment all of that out.
| | 05:31 | I highlight it and then I press Command then
the slash key on my keyboard here in Xcode
| | 05:36 | that comments things out.
| | 05:37 | I'm going to call for this little red line
is here, and again, we have a reference that
| | 05:41 | DetailViewController and just all of this
code in here we're not going to need this,
| | 05:46 | I'm just going to delete all of that.
| | 05:53 | There's a few commented out functions down
here, moveRowAtIndexPath, canMoveRow, we're
| | 06:00 | not going to be using any of those, I'm just
going to delete them and save, and this looks
| | 06:05 | like it needs to be saved
over here. Press Command+S.
| | 06:09 | We have a nice, slimmed down, ready to use,
and I'm going to save the storyboard as well,
| | 06:16 | and I'm going to go ahead
and press the Run button.
| | 06:18 | We have the iPhone selected, and we'll
notice that it compiles, and it runs just fine, it
| | 06:23 | says BW RSS at the top here.
| | 06:26 | So that was simple, we now have a
functioning table view controller and then all we need
| | 06:30 | to do is plug in the database calls.
| | 06:32 | The iOS Cocoa touch framework
actually makes this all pretty easy,
| | 06:36 | as we'll see as we move
forward building this app.
| | Collapse this transcript |
| Reading from the database| 00:00 | Now that we have a working table view, we
need to bring in our database library, so we
| | 00:04 | can display the feeds in our main view.
| | 00:07 | We'll start by making a working copy of the
BWRSS project from earlier in this chapter,
| | 00:13 | or you can copy from my BWRSS-02-done, like
I'm doing here, and I'm just going to rename
| | 00:20 | that to BWRSS-03-working.
| | 00:24 | I'm going to open into Xcode by
double clicking on the Xcode project file.
| | 00:28 | And the first thing we need to do is we need to
load in the libraries that we're going to be using.
| | 00:33 | We're going to start by loading in the
SQLite Framework so I'm going to select the Target
| | 00:37 | here and Summary and scroll down to where
we have the Linked Frameworks, press the plus
| | 00:44 | button and type in sqlite and select
sqlite 3.dylib, which stands for Dynamic Library.
| | 00:53 | And I'll click Add, and that
adds in the dynamic library here.
| | 00:58 | Now I'm going to bring in the libraries
from the exercise files so we'll come back over
| | 01:03 | here to the finder and under Exercise Files
in Libraries, I am going to select all three
| | 01:07 | of these BWDB, BWUtilities, and RSSDB--I'm
just holding down the command button on the
| | 01:14 | keyboard while I select those. And I'm
going to drag all of those into our BWRSS folder
| | 01:21 | here in our project, and when I let go over
that, you'll see we get this dialog box in Xcode.
| | 01:26 | I want to make sure Copy items is checked, Create
groups is checked, and the target BWRSS is checked.
| | 01:34 | And I'll select Finish, and
it copies those libraries in.
| | 01:38 | Finally, coming back out here to the libraries
folder in finder, I'm going to grab this BWRSS.db
| | 01:45 | file, this is the starting point database
that we created in our previous chapter, and
| | 01:51 | I'm going to drag that into our project as
well, and I'm going to make sure Copy items
| | 01:57 | and Create groups and the target are checked and
press Finish, and there we have database as well now.
| | 02:04 | So now we'll come into our header file, and I'm going to
import RSSDB.h header file and press Command+S to Save.
| | 02:14 | And I'm going to come over here in our View
Controller and up here in this interface section
| | 02:21 | here, this is where we declare local object
variables, and I'm just going to remove this
| | 02:26 | objects variable, and I'm going to
come in here and add some other variables.
| | 02:31 | We'll have a BOOL for iPadIdiom, and that'll
be a logical variable for checking if we are
| | 02:39 | on an iPad or a non-iPad iPhone.
| | 02:43 | A dictionary object, NSDictionary, and we
call this newFeed, and I'm using the underscore
| | 02:50 | for a lot of these, because that's
convention for instance variables in Objective-C and
| | 02:55 | RSSDB, this will be our database
object and then an array for the feedIDs.
| | 03:02 | We'll make a little space there, press Command+S
on my keyboard to Save my progress so far.
| | 03:09 | Now we're going to add database support.
| | 03:11 | First of all, I want to point something out
here, we'll get to this a little bit later.
| | 03:14 | You notice these red lines
that have appeared down here.
| | 03:17 | That's because I deleted that object variable,
and you notice that that's used throughout here.
| | 03:23 | We're going to get to that
and clean that up in a minute.
| | 03:26 | First I want to bring the database support,
and that comes in from Chapter03, this file
| | 03:32 | here, there's a text file here
with a bunch of Objective-C code in it.
| | 03:36 | Objective-C code tends to be a little bit verbose,
and so, rather than type a lot of this stuff,
| | 03:41 | and there's quite a bit of it,
we're just going to copy and paste it.
| | 03:44 | So starting there and coming down to just
before where it says view methods here on
| | 03:48 | line 52, so I'm going to select all the way
down to line 51, I'm going to press Command+C
| | 03:53 | to Copy and come back in here to Xcode, I'm
going to go all the way down here to the end
| | 03:58 | of the file just before where it says end, put my
cursor right there and press Command+V to Paste.
| | 04:05 | So that end needs to be at the end of the
file, and I've got all of this code that I
| | 04:10 | just added before that.
| | 04:11 | That's our database methods, and we'll be
looking into that a little bit more later.
| | 04:16 | Now let's come up here to where some
of these errors are, insertNewObject.
| | 04:19 | We don't need that anymore, because that
objects instance variable was just there as part of
| | 04:25 | the template for the table view application.
| | 04:28 | numberOfRowsInSection, we can find
that also in our text file over here.
| | 04:33 | I'm just going to grab that and copy that and paste
it in here, and scrolling down cellForRowAtIndexPath,
| | 04:43 | we're going to update that a little bit.
| | 04:45 | I'm just going to delete
these two lines for now.
| | 04:47 | We'll be getting back to this.
| | 04:49 | And finally, this whole commit editing style,
we don't need any of that, I am just going
| | 04:55 | to delete that whole thing.
| | 04:57 | Now I'm going to press Command+S to Save,
and I'm just going to press Command+B on my
| | 05:01 | keyboard here to Build and make sure that
this compiles, and you see the Build Succeeds,
| | 05:06 | and there aren't any issues.
So that's a good sign so far.
| | 05:09 | Now we've got just a few more things we need
to do in order to inform the table view about
| | 05:14 | the data so that the table
view can display the data.
| | 05:17 | There's a function in here called awakeFromNib,
and see I can select from this little selector
| | 05:23 | up here, it's a convenient feature of Xcode.
| | 05:26 | And we're not going to be using that because
we're not using a nib, we're using storyboards
| | 05:31 | instead, so I'll delete that method.
| | 05:33 | Under viewDidLoad, there's just another
line we want to add in here to display the edit
| | 05:38 | button in the navigation bar, and it look
likes this, self.navigationItem.leftBarButtonItem
| | 05:49 | = self.editButtonItem, and that will display
that edit button item in the navigation bar,
| | 05:56 | and I want to add a viewWillAppear method, and
we can find that again in our text file here.
| | 06:03 | Right there, I'm just going to grab that
and copy it and paste it into our Xcode.
| | 06:07 | I'm just going to put it
right here before this pragma.
| | 06:11 | And so when the view is just about to appear,
it checks to see if our current device user
| | 06:17 | interface idiom is the iPad, and if so,
it sets a little iPadIdiom flag to Yes.
| | 06:23 | Then it calls loadFeedIDs and
reloadData on the table view.
| | 06:28 | LoadFeedIDs is our own method down here.
| | 06:32 | We come down here into our database stuff,
and you see it's very simple, it simply checks
| | 06:37 | to see if we have a database,
and if not, loads the database.
| | 06:41 | That loadFeedDB will alloc and
init the database right there.
| | 06:46 | And then once it has the database, it comes
up to our feedIDs variable, and it loads that
| | 06:51 | by getting all of our feedIDs from the database.
| | 06:55 | And so what we end up with is this array,
and if we come up here to the top we can see
| | 06:59 | our instance variable that says NSArray of
feedIDs and is an array of the IDs of the
| | 07:07 | individual feed items, that are
going to be displayed in the table view.
| | 07:10 | Now if we come down here to number of sections
in table view, that's always going to be one,
| | 07:15 | because we only have one section and number
of rows and section, of course, we've loaded
| | 07:19 | that already, and that is the
count of the number of feedIDs.
| | 07:23 | And so finally, this cellForRowAtIndexPath,
that's where the actual cell is populated,
| | 07:30 | and before we can do that, I'm going to
press Command+S to Save what we've done so far.
| | 07:35 | We're going to go in to our iPhone storyboard.
| | 07:37 | That's this one here, and we just need
to set up this table cell a little bit.
| | 07:41 | So I'm going to scroll that over, and I'm
going to bring up our Inspector, and I'm going
| | 07:47 | to make sure I select the
Attributes Inspector, which is this one here.
| | 07:51 | The Table View Style is going to be
Subtitle, and we need an Identifier for it.
| | 07:57 | That Identifier is going to be FeedsCell,
and I'm just going to select it and press
| | 08:02 | Command+C, put it in my copy buffer,
because we're going to need that in just a moment.
| | 08:07 | And then I'm going to come down here where
it says Accessory, and I'm going to select
| | 08:10 | None, because we don't need
a disclosure arrow for that.
| | 08:13 | So I've got my main storyboard selected over
here, I'm going to press Command+S, and you
| | 08:17 | see that that now is showing saved, I'm
going to get rid of the inspector pane and come
| | 08:23 | back over here to our FeedsTableViewController,
and you'll notice where it says cellForRowAtIndexPath
| | 08:29 | method, and you notice that it says
dequeuReusableCellWithIdentifier,
| | 08:33 | and it says Cell, I'm just going to select
that and past that FeedsCell string.
| | 08:38 | These need to be exactly the same, and it's
really easy to misspell it--and I've on occasion
| | 08:42 | I've forgotten the S or something--and that
creates an error. I'll show you later on what
| | 08:46 | that error looks like in
case you actually get it.
| | 08:49 | So makes sure that we're
using this particular style.
| | 08:54 | So here we have a style
that's got the subtitle style.
| | 08:57 | Pressing Command+S again,
that was showing it wasn't saved.
| | 09:01 | And so it's making sure that it's using
the correct table cell from the storyboard.
| | 09:05 | Now we want to actually load our data here,
and so what happens is each time a table cell
| | 09:12 | is populated, the iOS system will call
this function in your application, and it will
| | 09:19 | pass you an index path that tells you
which cell it is that it's populating.
| | 09:23 | So we need to take that index path and use it to
find the correct data from our database. Okay?
| | 09:31 | And so first, I'm going to make sure that
I've loaded the FeedIDs, so I'm going to say
| | 09:35 | self loadFeedIDsIfEmpty, then
I'm going to Configure the cell.
| | 09:40 | So I need a dictionary item for the feedRow
itself, and I'm going to load that from the database.
| | 09:48 | So I'm calling getFeedRow on the
database and the row is this FeedIDs sub indexPath.row.
| | 09:57 | The indexPath.row, that's the particular
row in the table that's being populated.
| | 10:02 | And I use that to index into our array of
FeedIDs, and that will give me the correct
| | 10:07 | FeedID which I can pass to get feedRow. And
that will give me the dictionary entry that
| | 10:12 | I can load here, and now I've got the data all
loaded into my NSDictionary object, and I can use it.
| | 10:18 | I can say cell.textlabel.text = feedRow title, and I can
say cell.detailTextLabel.text = feedRow description.
| | 10:34 | Now when I save this, I'm pressing Command+S
on my keyboard, and I make sure I've got iPhone
| | 10:40 | Simulator selected there, and I press Run,
Build Succeeded, that's a really good sign.
| | 10:46 | Now it loads up the simulator, and there it's
displaying the data from our database in that table view.
| | 10:52 | So these are the entries that we loaded up
in the database in our previous chapter, the
| | 10:57 | one we populated the
database from the SQL schema.
| | 11:00 | So now we have a working
table view for our main page.
| | 11:03 | I just want to show you one more thing.
| | 11:05 | If we come back here into Xcode, and I'm
going to press Command+Period, and that
| | 11:09 | will stop--and you'll see it says Finish running--
that stops the app from running on the simulator,
| | 11:15 | and I'm just going to misspell this
FeedsCell identifier, so you can see what happens.
| | 11:20 | It's a really cryptic error, and if you
get this, I want you to know what it means.
| | 11:23 | So I'm just going to misspell that, I'm
going to press Command+S to Save, and I'm going
| | 11:27 | to press Run up here, and you notice that
instead of running, it gives us this error.
| | 11:32 | It switches us into Debug View over here in
the navigator pane, and it gives us our console
| | 11:38 | down here, and you'll notice that there is just a
whole bunch of junk showing there in the output.
| | 11:44 | If you scroll up to the top, you see that
there's an assertion failure, and it's in
| | 11:49 | this table view, dequeueReusableCellWithIdentifier
for IndexPath, and you'll notice that if we
| | 11:56 | scroll down some more, it says uncaught exception
and right over there, reason: unable to dequeue
| | 12:02 | a cell with identifier xFeedsCell.
| | 12:06 | And so, the error is buried in there, you
got a bunch of stuff before it and a bunch
| | 12:09 | of really pointless cryptic stuff after it.
| | 12:13 | But if you look through there carefully
enough you'll find what your error is.
| | 12:17 | And if we come back over here to the Project
Navigator and select our Table View Controller,
| | 12:24 | we see there is that string xFeedsCell, so
if I spell that correctly, it'll make it work.
| | 12:32 | So I'm saving with Command+S and pressing Run,
it says it's already running, it's actually
| | 12:38 | crashed, but that's okay, I'm going to click Stop,
Build Succeeded, and there we have it running properly.
| | 12:45 | We can minimize this
also with that button there.
| | 12:48 | Just so you know when you see
that error, that's what that means.
| | 12:51 | So now we have a working
table view for our main page.
| | 12:53 | We're reading feeds from
the database successfully.
| | 12:56 | We're formatting them nicely in our table
cells and displaying the data in the table view.
| | 13:00 | This is the foundation of our application,
and we'll be using this and building upon
| | 13:04 | it for the rest of the course.
| | Collapse this transcript |
|
|
4. Parsing XML DataUnderstanding the parsing process| 00:00 | XML is one of the most common
formats for raw data on the Internet today.
| | 00:05 | It's used for XHTML, SOAP, RSS, and
many other data delivery applications.
| | 00:11 | The iOS SDK provides a powerful event driven
XML Parser, like many iOS Services, it's event
| | 00:18 | driven, and it works with the delegate protocol.
| | 00:20 | The parsing process can take some time, so
we use a separate execution thread for it.
| | 00:26 | This allows the application to continue to
respond to touch events, while the parsing
| | 00:30 | takes place in the background.
| | 00:32 | This provides a better user experience as it
keeps the application from seeming unresponsive.
| | 00:37 | Before we can parse the XML, we need to get it
from the network using the NSURLConnection Class.
| | 00:43 | As we get the data from the network, we'll be
displaying the activity indicator in the status bar.
| | 00:48 | This gives the user some feedback so they know
that something is happening and data is coming.
| | 00:54 | Using the Activity Indicator is
not an excuse to block User Input.
| | 00:58 | The user must still be able to use the app.
| | 01:00 | They will expect the user
interface to still respond to input.
| | 01:05 | XML data is parsed and
added to a local database.
| | 01:08 | We store the data for two reasons: one, a
portable device has limited available memory,
| | 01:13 | so we cannot carry around a lot of data at once.
| | 01:16 | Storing data in the database freezes up
valuable RAM, while keeping data readily available.
| | 01:21 | And two, this gives our
application data persistence.
| | 01:26 | So the user can maintain a context between
sessions and they don't have to read and parse
| | 01:30 | the data from the net every time they need it.
So we drive the app from the database.
| | 01:35 | This allows the user to use the app without having
to wait for data from the net every time they need it.
| | 01:40 | This is the lifecycle of
the data in our application.
| | 01:43 | Read XML from the net, parse it, store the
results in the database, and drive the application
| | 01:48 | from the data in the database.
| | 01:50 | This is a very common model for a data driven application
as it allows the app to provide a great user experience.
| | Collapse this transcript |
| Creating the item view controller| 00:00 | Before we can get the item data from the
network for display, we need to create the table view
| | 00:04 | for the item view controller.
| | 00:06 | Let's start by making a working
copy of the BWRSS-XML-start project.
| | 00:11 | I'm going to Option drag this
and rename it to BWRSS-XML-02.
| | 00:19 | Now inside that folder, there is a file called
BWRSS.xcodeproject, I'm going to double-click
| | 00:25 | on that Xcode project file, and
that will open the project in Xcode.
| | 00:30 | We'll expand a couple of these disclosure
triangles, so we can see our source code,
| | 00:34 | and I want to select this BWRSS Group,
which looks like a folder in its icon there.
| | 00:40 | And I'm going to create a new class file.
| | 00:42 | We need to set up our Table View for the
items so we do that by creating a class.
| | 00:46 | I can either right click on this and say New
File, or I can just come up here in the File
| | 00:51 | menu and select New File.
| | 00:55 | Under iOS and Cocoa Touch, select
Objective-C class and press Next.
| | 01:01 | Now, I going to name the class,
BWRSSItemsTableViewController,
| | 01:07 | and that's a subclass of UITableViewController.
| | 01:10 | If UITableViewController is not selected there,
you want to select it from this drop down box.
| | 01:14 | We're going to leave both of these items
unchecked, we're not targeting for the iPad, and we're
| | 01:19 | not using a NIB File.
| | 01:21 | And I'll select Next and the default location
here is in this BWRSS folder, which corresponds
| | 01:26 | to this BWRSS Group over here.
| | 01:29 | We want to make sure that the BWRSS Group is selected,
and that it's targeted for the BWRSS Application.
| | 01:35 | So we select Create, and
there we now have our class files.
| | 01:40 | I'm just going to drag them up here so
that they're in with the other class files.
| | 01:44 | Now we're going to open the Storyboard
and create a segue for our new Table View.
| | 01:49 | So, I'm just going to open this up a little bit, so
we can see the storyboard_file name_iPhone_storyboard,
| | 01:56 | and because I have such limited real estate here
on the screen, I'm going to zoom out a little bit.
| | 02:02 | You probably won't need to do that, and there's a lot
of things that don't work right when it's zoomed out.
| | 02:06 | So don't, if you don't need to.
| | 02:08 | Then here, make sure that the Utilities Pane
is selected, and under Objects here, I'm going
| | 02:14 | to grab the Table View Controller, and I'm
just going to drop it right about there and
| | 02:21 | then I can adjust it so that it aligns properly.
It's not necessary, but it's a good idea.
| | 02:26 | Now I'm going to zoom back in here,
and I'm going to create as segue.
| | 02:31 | So I created a segue by control dragging from one
object that can be selected to our New Table View.
| | 02:38 | So, over here you see we
have the Feeds Table View.
| | 02:41 | If I scroll down you can see that
it's Feeds Table View Controller.
| | 02:45 | And I'm going to Control-click on the Table
Cell here and drag over towards the New Table
| | 02:53 | View, and you see that I get this little line,
and when I let go of the mouse button, I
| | 02:59 | get this Select a Segue, and I select the
Push type of segue, and it creates this segue.
| | 03:04 | So this here is the segue, and if I
click on that, then I can actually edit it.
| | 03:09 | Let me select over here the Attributes
Inspector, and I can give the segue an identifier.
| | 03:14 | So, I'm going to call this
Segue, SegueToItemsTableView.
| | 03:21 | I am going to press the Return key, because
that actually makes this take effect, sometimes
| | 03:24 | it doesn't happen if you type something into a
field in Xcode, and you don't press the Return key.
| | 03:29 | So, now our segue is named, and you see its
Style is Push, and so we've created the segue.
| | 03:36 | There are just a couple more of
things we want to do to our New Table View.
| | 03:39 | You'll notice down here it says Table View
Controller, and so if I select this, and I
| | 03:44 | select this orange circle here, which is
the Actual View Controller, and come back over
| | 03:48 | here to the Identity Inspector, you
notice that its class is UITableViewController.
| | 03:53 | We want our new sub-class to be
the Class for this table view.
| | 03:57 | So if I select this BWRSSItemsTableViewController, you'll
notice that now, it says Items Table View Controller.
| | 04:04 | If I select a different controller over here,
it will say Items Table View Controller, and
| | 04:08 | that's what you want it to say.
| | 04:10 | And that way it will be actually be using the code
in our BWRSSItemsTableViewController.m and .h file.
| | 04:18 | So you notice that this is grayed out over
here a little bit, this icon for our storyboard.
| | 04:22 | I'm going to press Command+S on my keyboard,
and that will take that gray away and make
| | 04:26 | it white, to indicate that it's saved.
| | 04:28 | I tend to press the Command+S key a lot,
sometimes I might forget to say that I'm doing that.
| | 04:34 | But it's an important habit to get into.
| | 04:36 | Now I'm going to select our Table Cell, and
if I come back over here to the Attributes
| | 04:42 | Inspector, under Style I'm going to select
Subtitle, make sure that the Accessory says
| | 04:48 | None, and then the reuse Identifier, I am going to
type in itemsCell, like that and press the Return key.
| | 04:56 | You notice over here in our Feeds Table View
that the cell has this little disclosure indicator.
| | 05:04 | We actually don't need that.
| | 05:05 | So I'm going to select that, under Accessory
in the same pane, I'm going to select None,
| | 05:08 | and you see that that goes away.
So now, we're all set up.
| | 05:13 | I am going to press Command+S to Save and come
back over here to our ItemsTableViewController.h
| | 05:19 | file, and the first thing I'm going to do in this .h
file is I'm going to import our database header.
| | 05:23 | So I'm going to come up here and type #import,
and I am going to type RSSDB.h, see I got
| | 05:32 | my code completion there, and Command+S to Save.
| | 05:36 | Now, when I come back out here to Finder,
and you'll notice we've got a few text files
| | 05:40 | here in our Exercise Files folder in the
Chapter04 folder. That's because Objective-C tends to
| | 05:46 | be pretty verbose, and we don't
want to have to type all this stuff.
| | 05:48 | So I am going to open this
header, this is 02-header.text file.
| | 05:53 | In my text editor--I'm using TextWrangler,
you can use any text editor as long as it's
| | 05:57 | a plain text editor, a coding
text editor and not a Word Processor.
| | 06:01 | I'm going to select these three lines here,
press Command+C to Copy, and come back over
| | 06:07 | here into Xcode, and I'm going to just select
this one line, here on line 12 and press Command+V.
| | 06:12 | Now, we have our interface property, property,
and end, and that's the way that that should look.
| | 06:18 | Now, press Command+S to Save.
| | 06:20 | Let's go ahead and close our Utilities pane,
so this all fits on my little screen here.
| | 06:25 | You can leave that open on yours.
I just have limited screen real estate here.
| | 06:30 | We can see that our class is
called BWRSSItemsTableViewController.
| | 06:35 | It's a subclass of UITableViewController, and
it implements the NSXMLParserDelegate protocol.
| | 06:43 | We have two properties declared, rssDB and
currentFeedID, and those are populated actually
| | 06:49 | from the FeedsTableViewController.
We'll see that in a little bit.
| | 06:54 | So, this header file is now complete.
| | 06:56 | I'm going to press Command+S to Save, and
we're going to open up the .m file for the
| | 07:01 | ItemsTableViewController.
| | 07:04 | As I scroll down here, you'll notice a couple
of these orange lines here, these are warnings.
| | 07:10 | We can just remove this safely.
| | 07:11 | We'll be editing these methods later on in
this chapter, but I'm just going to remove
| | 07:16 | the warnings for now.
Press Command+S to Save.
| | 07:19 | I'm going to come back out here to the Finder, and
I'm going to open this text file here, -02-methods.
| | 07:27 | This will open up in my text editor, and
you see we have quite a few things in here.
| | 07:31 | The first thing we're going to do is
select these class extension for private members
| | 07:35 | lines, and I'm going to press Command+C to
Copy. And come back over here into Xcode, and
| | 07:40 | I'm just going to select this line right here,
which is our interface declaration for a class.
| | 07:45 | And I'm going to press Command+V, and we'll
replace that line with what we've copied and
| | 07:50 | pasted from our text file.
| | 07:52 | So, we have a few variables here that are
being used as private members for our class,
| | 07:57 | and we'll talk more about each of them later on.
| | 08:00 | Going back over here to our text editor,
I've got some data properties, and again, these
| | 08:06 | are private variables inside our class.
| | 08:09 | I'm going to copy all of those, and I'm going to
paste those in right here, and press Command+S to Save.
| | 08:17 | Finally, down here we have
this viewWillAppear method.
| | 08:22 | I'm going to copy that, and I'm
going to paste that in right here.
| | 08:27 | Press Command+S to Save.
| | 08:29 | So you notice here in this code, one thing that we
do is we check the currentDevice userInterfaceIdiom
| | 08:35 | to see if we're running on an iPad.
| | 08:36 | If we are, we set this private member
variable iPadIdiom = YES, and you can see up here in
| | 08:43 | our class extension, we
have this BOOL iPadIdiom.
| | 08:47 | So, we can test that later on in the
code to see if we're running on an iPad.
| | 08:52 | One other thing you'll notice here, I'm using this
rssDB property and also this feedRecord property.
| | 08:59 | These are populated from the FeedsTableViewController,
and we'll get to that in a moment, and we're
| | 09:04 | using that to actually find
the current record for this view.
| | 09:08 | So, we select a row on the FeedsViewController,
it will bring this ItemsViewController already
| | 09:15 | populated with the items in that
feed, and this is how that's done.
| | 09:19 | So, if we go over here into the
FeedsTableViewController--make sure this is saved,
| | 09:23 | I'm pressing Command+S again--and we come
over here into the FeedsTableViewController,
| | 09:29 | and we select this prepareForSegue.
You notice that this is not populated yet.
| | 09:36 | Come back out here into Finder and open
this FeedsTableViewController-02-Support.txt in
| | 09:43 | our text editor and here's our
segue delegate for that file.
| | 09:46 | So, I'm going to copy and paste that into Xcode.
| | 09:50 | Just select this entire method here and
press Command+V, and we haven't yet imported this
| | 09:56 | header file, so we're
getting a couple of errors.
| | 09:59 | We'll take care of that in a moment.
| | 10:01 | You'll notice that this gets called when
a segue happens from this view controller.
| | 10:07 | This is the FeedsTableViewController, and it
gets passed this segue object, and that segue
| | 10:13 | object has a destinationViewController, and
so I can assign that destinationViewController
| | 10:18 | to a BWItemsTableViewController class
object and then I can actually populate
| | 10:26 | ItemsTableViewController.currentFeedID
and ItemsTableViewController.rssDB.
| | 10:30 | So if I come up here to the top then I'm just going to
import our header file BWRSSItemsTableViewController.h.
| | 10:42 | Now, when I come back down here to the prepareForSegue,
you see those errors have gone away, and we
| | 10:48 | are now assigning our FeedID and our rssDB to
the properties in the ItemsTableViewController.
| | 10:55 | I press Command+S to Save this, and we see over
in our ItemsTableViewController in ViewWillAppear,
| | 11:02 | this is where we're using those variables.
| | 11:03 | We are also over here setting our row height
so that we have enough height to display the
| | 11:09 | things that we want to display.
Now, I can just build this.
| | 11:13 | I'm going to press Command+B to Build.
| | 11:15 | Before I run in the simulator, I want to
make sure I've got the iPhone simulator and not
| | 11:19 | the iPad simulator selected, and then I can just press
Run, and this will run it in the simulator. There we are.
| | 11:27 | This is the 4-inch Retina iPhone simulator, and
this is scaled down because this screen is so small.
| | 11:32 | It doesn't have enough
pixels to actually support that.
| | 11:35 | Hopefully you're running on a bigger screen.
So, this doesn't have any borders.
| | 11:38 | It doesn't have a Home button,
but this is the iPhone simulator.
| | 11:41 | Yours will probably look different, and
when I select one of these feeds here, you see
| | 11:46 | it brings up our new ItemsTableViewController,
and you can see there's the title that gets
| | 11:51 | populated right here, self.title = self.feedRecord
(@"title"), so it's actually reading that
| | 11:56 | from the database based on the current feedID that got
passed from the FeedsTableViewController. So, there it is.
| | 12:05 | That is completely working.
I can bring up the lynda blog.
| | 12:07 | Of course, none of these
are being populated yet.
| | 12:11 | That will happen in one of our later movies, but
we now have a working table view for the items.
| | 12:16 | We're using the segue from the storyboard
to set up the ItemsTableView, and now we are
| | 12:20 | ready to fill in the ItemsTableView with
some data, and we'll do that in the next lesson.
| | Collapse this transcript |
| Reading data from the internet| 00:00 | Because of the data that we're using comes
from the network, we need to create a network
| | 00:04 | connection and read that data
before we can parse it and use it.
| | 00:07 | For this, we'll use the NSURLConnection class.
| | 00:12 | Start by making a working copy of BWRSS-XML-02,
or in this case, the -done version.
| | 00:18 | You can use your version from the previous
lesson if you have it, which would not have
| | 00:22 | this -done at the end.
| | 00:24 | So, I'm going to make a working copy of this,
and I'm going to rename it BWRSS-XML-03, and
| | 00:32 | I'm going to go ahead and open the project
by double-clicking on this Xcode project file.
| | 00:37 | We have a working table view here, so now
it's time to set up the NSURLConnection class,
| | 00:43 | so we can grab the feed from the network.
| | 00:45 | We're going to start by importing the BWUtilities,
because we're going to use that for our error displays.
| | 00:55 | We're going to come back out here to Finder,
and we're going to grab a bunch of codes from
| | 00:58 | this 03-NSURLConnection.txt file.
| | 01:03 | This just has a lot of stuff in it that
we don't want to have to type right now.
| | 01:07 | We're going to start by
grabbing a bunch of these constants.
| | 01:10 | These constants are used
for a number of purposes.
| | 01:12 | We'll take a look at them in a moment here.
| | 01:15 | Copy that and just going to paste all of that
right in here at the top of the Source file.
| | 01:21 | This should be right after these imports and
right before this class extension for private members.
| | 01:28 | So these constants are used throughout the
code for a lot of different purposes, both
| | 01:32 | for the URL connection and for the parsing
code, and it's just that we don't have to
| | 01:37 | type a lot of these literal strings and having them
all in one place makes it easier to make changes later.
| | 01:43 | So, this is just good programming
practice, to use constants instead of literals.
| | 01:48 | So, I'm going to press Command+S to Save
that and come back out here to my text file, and
| | 01:53 | I'm going to grab everything
from here all the way to the end.
| | 01:58 | We're just going to paste this in, and
we'll look at it as we go through it.
| | 02:02 | So, I'm going to copy, and I'm going to come
down here all the way to the end of our code
| | 02:06 | and just before the end marker there, I'm
going to paste this all in, and you can see
| | 02:14 | here, starting with our Support methods, we
have loadRSSFeed, which creates the NSURLRequest
| | 02:20 | object, and it uses the URL from the feedRecord.
| | 02:26 | And then it sets up the connection with the
delegate as the self, that allows us to use
| | 02:30 | all of these NSURLConnection delegate methods.
| | 02:33 | It sets our networkActivityIndicator that's
the little spinner in the bar at the top of
| | 02:38 | the screen on our iOS device.
| | 02:41 | Once that's started, then the NSURLConnection calls
these delegate methods, which are again very simple.
| | 02:49 | Set up the connection, we didReceiveResponse,
we set that networkActivityIndicatorVisible
| | 02:53 | again, and we set up a place to store the data.
| | 02:57 | Every time we receive data, we go ahead and appendData
that accumulates it in our NSMutableDate object.
| | 03:05 | When we're done, we'll go ahead and
detachNewThread to parse the data.
| | 03:09 | Now, we don't have our parser code set up
in here yet, so I'm going to comment that
| | 03:13 | out and in the event of an error, I have this
didFailWithError, we go ahead and call our error code.
| | 03:20 | So, we have this handlError down here and
the errorAlert, which uses the alertMessage
| | 03:25 | method from the BWUtilities, or if we have more data
to display, it goes ahead and creates a UIAlertView.
| | 03:32 | So, in order for this to work, we
need to call all this code some place.
| | 03:37 | So, we're going to come back up here to our
viewDidLoad--and I'm just going to take out
| | 03:44 | all of these comments here--and I'm going
to go ahead and call our loadRSSFeed, self
| | 03:51 | loadRSSFeed, and we will compile, I am
pressing Command+B on my keyboard. Build Succeeded.
| | 03:59 | Make sure the iPhone Simulator
is selected up here and press Run.
| | 04:05 | Now when I click on this, you see a little
network indicator came up, but we don't have
| | 04:08 | any indication yet that anything is happening.
| | 04:11 | You can see the network
indicator kind of flash up there.
| | 04:14 | So, let's go back here in the code, and
I'm going to press Command+Period to stop
| | 04:19 | the Simulator. And in our DidFinishLoading
in the URLConnection delegate, you notice
| | 04:27 | where I commented out this detachThread for
calling our parser that we don't have yet,
| | 04:32 | I'm going to put in an NSLog, so we can see
that we got to this point, which means that
| | 04:38 | we have finished loading
the data, and we have data.
| | 04:40 | I'm just going to type have data in the NSLog,
press Command+S and press the Run button,
| | 04:47 | and now when I click on one of these,
you see we get that have data down here.
| | 04:51 | Just make a little more room for that, and
I'll go ahead and click on another one, we
| | 04:56 | have data, and I'll click
on another one, we have data.
| | 04:59 | We see our little network indicator coming up
there on the Carrier Bar at the top of the Simulator.
| | 05:04 | So, I'm going to switch back to Xcode.
| | 05:06 | Press Command+Period, and
we've stopped running the Simulator.
| | 05:10 | Now, our NSURLConnection is complete.
| | 05:13 | You can see it's pretty simple to
use or at least it's a bit demystified.
| | 05:16 | The delegate classes tend to appear less
intimidating over time as you get to know them.
| | 05:21 | Now, we are successfully reading the feed
using our NSURLConnection object, and we have
| | 05:27 | RSS data that we're ready
to parse for the Item View.
| | Collapse this transcript |
| Parsing the feed with NSXMLParser| 00:00 | Parsing XML is a processor intensive operation and
mobile devices tend to have limited processing power.
| | 00:07 | Even though Apple continues to improve the
specs of each iteration of their devices,
| | 00:11 | in order to preserve the responsiveness that
these devices are known for, we'll be doing
| | 00:15 | our parsing in a separate execution thread.
| | 00:18 | This ensures that we don't block the main
thread, which could cause the user interface
| | 00:22 | to freeze during parsing.
| | 00:24 | Let's start by making a
working copy of BWRSS-XML-03.
| | 00:28 | I'm going to use the -done version here,
and I'm just option dragging to make a copy
| | 00:34 | and rename that to -04.
| | 00:37 | I'll open the project in Xcode by
double-clicking on this Xcode project file.
| | 00:44 | Now, remember we have this NSLog line, which
says we have data, and that happens when we
| | 00:50 | finish loading from the NSURLConnection.
| | 00:52 | So, if I run this in the iPhone Simulator,
I select the iPhone Simulator and click Run.
| | 01:00 | When I click on one of these feeds, you'll
notice we'll get a message down here in the
| | 01:04 | log that says have data.
Click on another feed, have data.
| | 01:10 | So, I'll go ahead and stop this from running in the
Simulator by selecting Xcode and pressing Command+Period.
| | 01:18 | I'm just going to
minimize our log area down there.
| | 01:21 | Now, we're going to bring in a whole bunch
of code in our text editor, because it's just
| | 01:25 | way too much typing for us
to do here in this lesson.
| | 01:28 | So, you'll see the file here
TableViewController-04-NSXMLParser.txt.
| | 01:34 | I'm going to open that in my text
editor, I'm using TextWrangler here.
| | 01:37 | I'm going to go ahead and select all of the
file, you'll notice that there's just a whole
| | 01:42 | lot of code in here, and we'll go
through this, and we'll take a look at it.
| | 01:46 | We're going to do that in Xcode. So, I'm going to
Copy that with Command+C and select Xcode.
| | 01:51 | I'm going to come all the
way down here to the end.
| | 01:53 | I'm going to put this in just before the Error
handling here, and I'll Paste that with Command+V
| | 02:02 | and press Command+S to Save.
| | 02:04 | So, if we look at what we've added to the file
here, we have the NSLXMLParser delegate methods.
| | 02:10 | So, the way this works is when we set up
the Parser, we set our own class as the parser
| | 02:17 | delegate and then the parser calls the
delegate methods inside of our class at each step in
| | 02:25 | the parsing process so
that we can process the data.
| | 02:29 | We have a few little Utility
methods, and we'll look at these.
| | 02:31 | These mostly have to do with formatting dates
and these error handlers were in there before.
| | 02:35 | So, if we look at didStartElement, this is a
parser delegate that's called at the beginning
| | 02:41 | of each element in an XML stream.
| | 02:43 | Basically, what we do at the beginning of an
element, depending on the type of an element,
| | 02:47 | if it's a container, we just set things up
to start accumulating data, and if it's not
| | 02:52 | on a container, then we decide
what to do individually based on that.
| | 02:56 | For instance, if it's the ChannelElement, we
start setting up the feed and item objects.
| | 03:02 | If it's an ItemElement, then we know that
we've completed the item before, and we can
| | 03:07 | use the contents of that item
to start populating the database.
| | 03:12 | If it's one of our containerElements
then we just start accumulating data.
| | 03:18 | DidEndElement tells us that we've gotten to
the end of the element and especially in the
| | 03:21 | case of the containerElements, it tells us
that we can start adding items to the list,
| | 03:26 | because we've reached the end of a container
and all of the content of that container that's
| | 03:30 | been accumulated, we can start
adding that to the Items List.
| | 03:35 | In particular, if we have one of these dateElements,
so these are really just different names for
| | 03:40 | the PubDateElement, but then we need to clean
up the date format, and if we come down here
| | 03:46 | into our Utilities and look at dateStringToSQLDate,
of all these different possible formats
| | 03:52 | that I've discovered in RSS Feeds,
and I know that there's more.
| | 03:56 | There are probably still some that don't work with
this app, because they have these different date formats.
| | 04:01 | They are supposed to use a standardized format
for their dates, but everybody uses a different
| | 04:06 | format, and I don't know why.
| | 04:07 | It's just annoying mostly,
but this is how we deal with it.
| | 04:10 | So, we parse in from these different dates,
we use the built-in dateFormatter in iOS.
| | 04:16 | It's the same as in OS X and then we simply
format it into the standard SQL format for
| | 04:22 | storing in the database.
That makes it possible.
| | 04:25 | Compare dates, it makes it possible to sort dates, because
this is a really sensible format for computer processing.
| | 04:32 | Now, especially for our containers whenever
we find characters, we accumulate that and
| | 04:38 | at different points in the process we say,
okay, we're done with this chunk of data,
| | 04:42 | and we'll go ahead and process it.
| | 04:44 | And if there is an error, then we call handleError
on the main thread and of course, handleError
| | 04:50 | is the same as we've used elsewhere in this
application for, especially in the NSURLConnection methods.
| | 04:57 | So, we come back up here to our Support
methods, parseRSSData that sets up the NSXMLParser
| | 05:06 | in a separate thread.
| | 05:08 | You notice that we have to put all of this
code in this autoreleasepool that's because
| | 05:12 | it's running in the secondary thread, and
this is the new syntax for doing this with
| | 05:16 | ARC, Automatic Reference
Counting, starting in iOS 5.
| | 05:20 | In our addItemsToList method, this
is where we add items to the database.
| | 05:26 | Now because we haven't set up our Table methods yet,
we're not going to be displaying this on the screen.
| | 05:30 | So, we're going to put in here an NSlog so
that we can see when we actually have the
| | 05:37 | parser data, because we're not going to be
actually displaying it on the screen yet,
| | 05:41 | because we haven't set up those
methods yet for handling the table.
| | 05:44 | We'll do that in the next movie.
| | 05:46 | For right now we just want to say
item and use that for the title.
| | 05:50 | We'll put the publication date in parentheses,
and we'll put the URL in square brackets.
| | 05:57 | I'm a big fan of copy and paste.
| | 06:00 | We're going to use our constants for keying off the
Item here, kItemTitleKey and kItemPubDate and kItemUrlKey.
| | 06:14 | It's looking like it's got a
little warning here. Ah, yes, typo.
| | 06:21 | Here we go and press Command+S to Save.
| | 06:25 | Now, we need to come back here to
connectionDidFinishLoading, and you
| | 06:31 | remember we have this NSLogin
there that says have data.
| | 06:33 | I'm going to go ahead and take that out, and I'm
going to uncomment this detachNewThreadSelector
| | 06:39 | so that we can go ahead and run
the parser in our secondary thread.
| | 06:43 | So you see there's our call to parseRSSData as the
selector getting passed to detachNewThreadSelector,
| | 06:49 | Target:self, Object: self.rssData.
| | 06:52 | I'll go ahead and press Command+S, and we are
going to Build, I'm going to press Command+B
| | 06:58 | on my keyboard here and
Build Succeeded. No problems.
| | 07:01 | We can go ahead and run
this in the iPhone Simulator.
| | 07:04 | Now, when I click on one of these, you see
down here we got our parsed data, and you
| | 07:14 | can see here is item: On Choosing a Programming
Language, that's the title, there's the publication
| | 07:20 | date in the parentheses there.
| | 07:22 | Down here in the square brackets,
that's the URL associated with that.
| | 07:26 | So, I can go ahead and click on one of these
other feeds, and you see we got a lot of data
| | 07:30 | there for that feed.
| | 07:35 | I can click on the final feed,
and we get even more data.
| | 07:39 | So, now we're successfully parsing the feed,
and we're updating the Item Table in the database
| | 07:45 | with the Items from the RSS Feed.
| | 07:47 | At this point, all that's left to do is to
update the TableView with the Items from the
| | 07:51 | database just as we did with the
feeds in the FeedsTableViewController.
| | 07:54 | So, I'll go ahead and quit the Simulator and
minimize this and get ready for the next lesson.
| | Collapse this transcript |
| Updating the item view with the feed items| 00:00 | At this point, we have a working parser running in a
separate thread that's updating items in the database.
| | 00:05 | Now, it's time to update the ItemTableView
from the database, and now that all the heavy
| | 00:10 | lifting has been done,
this becomes pretty simple.
| | 00:14 | We'll start one by making a
working copy of BWRSS-XML-04.
| | 00:18 | In this case, I'm going
to use the -done version.
| | 00:22 | You can use the one that you completed in the last movie,
if you've been following along with the exercises.
| | 00:27 | I'm going to rename this to BWRSS-XML-05, and
I'm going to open it in Xcode by double-clicking
| | 00:34 | on the Xcode project file.
| | 00:39 | I'm going to go ahead and expand this and
select the iPhone Simulator there and select
| | 00:45 | the ItemsTableViewController here.
| | 00:48 | I'm going to minimize this Utilities Bar,
and that way we have plenty of screen real
| | 00:54 | estate here to look at our code.
| | 00:56 | So, you notice as we add items to the database here,
we have this little NSLog that displays the items.
| | 01:05 | If I go ahead and run this in the Simulator--
| | 01:08 | I've selected the iPhone
Simulator, and I'm going to run this.
| | 01:13 | You see that as I select a feed, we get the output
down here in the log, and that's from this NSLog here.
| | 01:22 | So, we have the item title, we
have the pubDate, and we have the URL.
| | 01:31 | I'm going to stop the Simulator by pressing
Command+Period, and we'll go ahead
| | 01:36 | and minimize this so
that we have some room here.
| | 01:39 | Now, what we have to do is to
update the TableView from the database.
| | 01:43 | So, there are couples of steps to that.
| | 01:45 | We're going to come back up here to our
Table view data source section and the number of
| | 01:51 | sections in the TableView is going to be
one because there's always just one section.
| | 01:56 | We don't use the Sections
Feature for this application.
| | 02:00 | Number of rows in section
is already filled out here.
| | 02:03 | We have the itemRowIDs getting updated from
the getItemIDs in the database and then we
| | 02:10 | return the count and so that's
the number of rows in the section.
| | 02:13 | I'm going to press Command+S
here on my keyboard to save.
| | 02:16 | Now, our cellForRowAtIndexPath, this is a
bit of code, so we're going to go ahead and
| | 02:22 | read this from a txt file, coming back
here into Finder, our 05-Table.txt.
| | 02:27 | We're just going to grab this whole thing
because this is all just that one method.
| | 02:33 | I'm going to select all with Command+A and
Command+C to copy and come back here into
| | 02:39 | Xcode, and I'm going to select this
method here, cellForRowAtIndexPath.
| | 02:45 | Select the entire method and paste and
then we can look at the code here in Xcode.
| | 02:50 | I'm going to press Command+S
on my keyboard to save.
| | 02:56 | Our CellIdentifier, we want to come back
over here into the iPhone storyboard and select
| | 03:03 | our cell and bring up our Utilities View.
| | 03:08 | You see the Identifier here
says ItemCell, all right?
| | 03:11 | If we come back over here to our TableViewController, we
see that it says ItemCell, so it does match. That's important.
| | 03:18 | If it doesn't match, we know
that the cell won't get updated.
| | 03:22 | Now, we get the FeedItem based on the indexPath.row, and
now we have what I call the clever variable font size trick.
| | 03:32 | It's really very simple.
| | 03:33 | All we're doing here is we're taking the text
that's going to go in the ItemCell, and we're
| | 03:38 | looking at whether it would take up more
than one line, and if it takes up more than one
| | 03:43 | line, then we're using a smaller font size,
and we're using a larger font size if it takes
| | 03:49 | up just the one line.
I'll show you that when we run this.
| | 03:53 | Again, we're reformatting the date.
| | 03:56 | This time we're taking an SQLDate, and
we're making it into a displayable date, and if
| | 04:02 | we look down here in our Utility methods,
SQLDateToDate, that puts it in an NSDate format
| | 04:08 | and then we have dateToLocalizedString,
which puts it in a localized format.
| | 04:13 | If we come back up here to our cellForRowAtIndexPath,
you see we call dateToLocalizedString, based
| | 04:22 | on the SQLDateToDate, and that all
gets loaded into the detailTextLabel.
| | 04:27 | Now just before we run this, we want to come
back up here to addItemsToList, and we just
| | 04:33 | want to take out this NSLog line here.
| | 04:37 | We're not going to need that anymore
because we're now updating the TableView.
| | 04:39 | So, I'll save this, and we've got iPhone
Simulator selected, and we'll go ahead and we'll run
| | 04:44 | this in the Simulator, and I'll select
one of these feeds, and there we have it.
| | 04:49 | Now, we have our data being displayed, and you'll notice
we have the title and the date in the localized format.
| | 04:57 | If I go ahead and look up one of these other entries,
you'll notice that we have two different font sizes.
| | 05:03 | This line here InDesign FX is in a larger
font than these that have more than one line.
| | 05:09 | What can happen here as well, I don't have
an example of it here, but I think I have
| | 05:13 | an example of it in this one.
| | 05:15 | You'll notice that some of these are in a
smaller size because they wouldn't fit all
| | 05:19 | on one line in the larger size.
| | 05:21 | So, this one is a larger size, this one is
a larger size, and these three are actually
| | 05:25 | the smaller size and they don't wrap around
to the second line, but if they were in the
| | 05:30 | larger size, they would and so they
get that smaller font size anyway.
| | 05:35 | So, that's that clever font-size trick
that's used in cellForRowAtIndexPath.
| | 05:39 | Now, we have a working
TableView for the feed items.
| | 05:43 | It reads the RSS feed from the net, it parses
it in a separate thread, it stores the results
| | 05:48 | in the database, and it reads the results
from the database for display in the TableView.
| | 05:52 | We'll implement a WebView for
displaying the items in a later chapter.
| | Collapse this transcript |
|
|
5. Using a Modal ViewUnderstanding the modal view| 00:00 | On the main view of the BWRSS app
you will see a button with a plus sign.
| | 00:05 | This is for adding a new feed.
| | 00:07 | When you press on the Add Feed button, a modal
view appears with the form for adding the feed.
| | 00:12 | This is a common visual
model for an add item action,
| | 00:15 | so it will be familiar to the user.
| | 00:18 | It's a modal view because it's temporarily
displayed on top of another view, and it must
| | 00:22 | be dealt with before
returning to the parent view.
| | 00:26 | A modal view is typically used to present an
edit page or additional details of a model object.
| | 00:32 | Take care to always include clear and
prominent completion and cancel buttons.
| | 00:37 | In this case, the
completion button is labeled Add.
| | 00:40 | A modal view is a very different kind of animal.
| | 00:43 | When you're using a modal view,
you'll want to keep a few things in mind.
| | 00:47 | Never display an alert or any
other modal object from a modal view.
| | 00:52 | The danger is that the modal view may be
dismissed before the alert, and this would leave the
| | 00:56 | alert orphaned and cause your app to crash.
| | 01:00 | Never communicate directly with another
view object, including your parent view.
| | 01:04 | You may be tempted to pass a view object
pointer to your modal view to make it easy to send
| | 01:09 | messages back and forth.
| | 01:11 | Again, it's just too easy to orphan your
pointer, leaving a nasty memory leak or worse.
| | 01:17 | The easy thing to do is to create a
delegate protocol and communicate with that.
| | 01:22 | I know this sounds complicated, but it's
actually very easy to do in Objective-C, and I will
| | 01:26 | show you how in this chapter.
| | 01:28 | A modal view is a powerful tool and like
any powerful tool, it must be used with care.
| | 01:33 | Visually, it's a strong indication to the user
that it is necessary to enter some information
| | 01:38 | or take some action immediately.
| | 01:40 | You just need to make sure that there is a
very clear path out of the modality, and that
| | 01:44 | it's easy to get back to the normal workflow.
| | 01:49 |
| | Collapse this transcript |
| Constructing the view controller| 00:00 | At this point, we have a working FeedsTableViewController for
viewing feeds and a working ItemsTableViewController for viewing items.
| | 00:08 | In this movie, we will create the
AddViewController for adding feeds to the database.
| | 00:13 | This will be accomplished by creating a modal
dialog view for getting the feed URL from the user.
| | 00:19 | We'll start by making a working copy of
BWRSS-addView-start, and we'll call it BWRSS-addView-02.
| | 00:25 | I'm just going to expand this a little bit.
And there is our addView, and that's our start,
| | 00:33 | and I make a copy, and I rename it with 02.
And I'm just going to open the Xcode by double-clicking
| | 00:38 | on this Xcode project file.
| | 00:42 | And now, before we can create the screen
in storyboard, we need to create a new class
| | 00:46 | to support the AddViewModal dialogs.
| | 00:48 | So, I'm going to select
our BWRSS folder up here.
| | 00:52 | You could just right click on this and say
New File, or you can go up to the File menu
| | 00:57 | and say New > File; either
one does exactly the same thing.
| | 01:02 | And we're going to select a Cocoa Touch Objective-C class,
press Next, and this is BWRSSAddViewController, like that.
| | 01:12 | And it is a subclass of UIViewController,
so you want to select UIViewController and
| | 01:18 | not UITableViewController, if
that's the last one that you used.
| | 01:21 | It was apparently the last one that I used.
| | 01:24 | And leave both of these unchecked.
| | 01:25 | It is not going to be targeted
for the iPad. We're not using NIBs.
| | 01:28 | And I'll select Next, and it will get stored
right there in the BWRSS folder with all the
| | 01:35 | other m and h files. And you want to make
it in the BWRSS group and make sure that the
| | 01:40 | BWRSS application is targeted so that
check box needs to be checked there.
| | 01:46 | I'll press Create, and there
we have our new class files.
| | 01:50 | I'm just going to drag them up
above that Supporting Files folder.
| | 01:54 | And I'm going to open the .h file,
and we'll get started in there. And switching
| | 01:59 | back to my Finder, you see here that
I have an AddView02-header.txt file.
| | 02:05 | So, I'm going to open that up, and I'm just going to
select all of these and drop it in this Xcode file.
| | 02:11 | I'm just going to select these there and
make sure our ends are matched up; they are.
| | 02:17 | And so there are a couple of
interesting things going on here.
| | 02:19 | You will notice that we have a protocol declared.
This is the RSSAddViewControllerDelegate protocol.
| | 02:25 | This has three methods defined in it.
And we're going to be implementing that towards
| | 02:30 | the end of the chapter, but that allows
the AddViewModalViewController to communicate
| | 02:36 | with the FeedsTableViewController.
| | 02:38 | The FeedsTableViewController is going to be
the parent of this ModalViewController, but
| | 02:43 | because this is a ModalViewController,
we don't want to do things like dealing with
| | 02:46 | the database directly;
| | 02:47 | we want to communicate back to the parent
ViewController, which is not modal, and allow
| | 02:52 | it to do those time- and
resource-intensive things.
| | 02:55 | Otherwise, we end up affecting the
performance of the rest of our device.
| | 02:59 | And in fact, iOS even prevents us from doing
certain things from a ModalDialogueViewController.
| | 03:04 | So, we had this protocol. And we'll see that it
adds a layer of complexity, but it's not that bad.
| | 03:09 | You will see it is relatively easy to implement.
| | 03:12 | Our AddViewController itself will be using the
NSURLConnectionDelegate and NSXMLParserDelegate protocols.
| | 03:18 | We have this delegate property, which actually
gets populated from the FeedsTableViewController,
| | 03:24 | and we'll see how to do
that later in the chapter.
| | 03:27 | And then we have a TextField,
a Label, and a couple of Actions.
| | 03:31 | We'll be hooking those up
to our ModalViewController.
| | 03:33 | So, I'm going ahead and press Command+S to Save,
and I'm going to switch to the .m file.
| | 03:38 | I just did that by pressing
Ctrl+Command+Up Arrow here on my keyboard.
| | 03:43 | You'd also just select it
in the Project Navigator.
| | 03:45 | Of course, you may want to
be using the Assistant Editor.
| | 03:49 | I'm not using the Assistant Editor because
I have such limited screen real estate here.
| | 03:53 | So, over here in the .m file, or
methods file, I'm going to go back to the Finder
| | 03:58 | and I'm going to bring up the stuff that
we don't want to have to type a lot of.
| | 04:02 | You can see there is quite a bit of that.
| | 04:05 | Now, first, we have this import
BWUtilities because we used some of those utilities.
| | 04:10 | So, I'll go ahead and I'll
paste that in right there.
| | 04:15 | And we have a whole lot of constants,
and we'll be using these throughout this chapter
| | 04:21 | and throughout this class.
| | 04:22 | So, I'm just going to go ahead and
select all the way to the end here,
| | 04:25 | and I'm going paste this in, in Xcode.
| | 04:28 | I'm going to select these and replace that.
| | 04:31 | Notice we have this warning here. That is
probably for an incomplete implementation
| | 04:35 | because there are things that we had declared in the
header file that we haven't implemented in here yet.
| | 04:40 | So, we have a number of constants that we
are using for various different purposes.
| | 04:43 | It's always a good idea to use a constant
rather than typing in a literal string or
| | 04:47 | a literal number, especially if you're
going to be using that literal more than once.
| | 04:52 | We are also declaring a
number of instance variables here.
| | 04:55 | We have an enum that we use
for a mode switch later on.
| | 04:59 | The xmlData is used for accumulating data
as we read it from the network connection,
| | 05:04 | currentElement is used during parsing, feedRecord
is used to accumulate data for the feedRecord
| | 05:09 | before we pass it back to the parent
controller, and a number of flags here, and some local
| | 05:15 | properties as well.
| | 05:18 | We also have a number of methods that we're
going to go ahead and just drop these in right now.
| | 05:23 | And so I'm going to copy that and come down
in here and paste them just before this @end.
| | 05:32 | We have a duplicate here of the viewDidLoad,
so I'll go ahead and I'll delete that one.
| | 05:37 | So, viewDidLoad, it sends a
becomeFirstResponder message to the URL text feed.
| | 05:44 | We'll see what that does in a little while.
| | 05:46 | It makes that TextField the
focus as soon as the view is loaded.
| | 05:51 | And we have actions for our Cancel button
and our Add button, and we also have this
| | 05:55 | textFieldShouldReturn, which intercepts the
Return button, or the Return key from the keyboard,
| | 06:01 | and allows us to take an action based on it.
| | 06:04 | We'll see how that works as well.
| | 06:05 | So, I'm going to go ahead and
press Command+S to save this,
| | 06:09 | and now we're going to create
the interface in our storyboard.
| | 06:13 | So, I'm going zoom out here a little bit
because this is just too big to work with in this
| | 06:19 | small amount of real estate
I have here on the screen.
| | 06:21 | And I'm going to bring out our Utilities
pane. And the first thing I'm going to do is I'm
| | 06:27 | going to come over here to the
FeedsTableViewController and I'm going to add a button.
| | 06:32 | And so I'll come down
here into Windows and Bars.
| | 06:37 | There it is, Bar button.
| | 06:39 | I'm going to drop that on to--yeah, it
looks like I need to zoom in in order to do that.
| | 06:43 | I'm going to drop that on there and under
Style, Bordered is fine, Identifier, Add,
| | 06:50 | and that will give it a little plus sign.
| | 06:53 | And I'm going to zoom out again, and I'm going
to create a new Controller. Under Controllers
| | 07:01 | & Objects, I just want a Plain View Controller.
| | 07:05 | Now, I want us to get under this
TableViewController here, and I'm going to select both of them
| | 07:13 | and scroll them up a little bit.
| | 07:18 | And I'm going to create a new segue from
this plus button that I've just created.
| | 07:23 | So I'm holding down the Control key on my
keyboard while I drag a line from here to
| | 07:27 | our new ViewController, and I'm
going to select modal for the segue.
| | 07:32 | Now, we have our new segue here,
and I'm going to select that.
| | 07:38 | Its identifier will be SegueToAddView.
| | 07:44 | Its style is Modal, and its
Transition will be Cover Vertical.
| | 07:49 | I'm going to press the Enter key
there and press Command+S to save.
| | 07:54 | Now, we're going to zoom in to our new
ViewController here, and I want to select the orange circle
| | 08:02 | there which identifies our controller and
come over here to show the Identity Inspector,
| | 08:06 | and we're going to select our new
BWRSSAddViewController for that.
| | 08:11 | And so now we can see
it's the AddViewController.
| | 08:15 | And I want to set some properties on this
controller--make sure I've got the Controller selected.
| | 08:22 | I'm going to get a Scroll View and drop that
on here so that we can add some items to that.
| | 08:34 | So, there's our Scroll View,
get that nicely centered in there.
| | 08:37 | And I'm going to select the Background and
make it the Scroll View Textured Background Color.
| | 08:44 | Now, the thing about the Scroll View Textured
Background Color is that it doesn't actually
| | 08:48 | show up here on this screen,
but it is a dark color.
| | 08:51 | So, I'm going to be putting items on it.
I'm going to setting their color to white so that
| | 08:54 | we can actually see them.
| | 08:56 | And this will make sense when we go to
test it, and you'll see what it looks like.
| | 08:59 | But in the meantime, it's going to make things
a little bit difficult to work with, and that's
| | 09:02 | just a quirk of how Interface
Builder works and how storyboards work.
| | 09:07 | So, I'm pressing Command+S to save.
| | 09:09 | I do that a lot. And I'm going to grab a text
Label here, under Controls, and Label. I will
| | 09:17 | bring it up to the top here and center, or
you can bring it up to the top and left, and
| | 09:23 | you can just drag its right side
out all the way to the other line.
| | 09:27 | So, I'm using these guides
to help to line things up.
| | 09:31 | And it makes it so that everything looks like
nice on the screen when we're done laying it out.
| | 09:35 | I'm going to select a Center
Alignment and the System Bold Font.
| | 09:41 | The Size of 17 is good, and it is going to say Add
RSS Feed, and you can see that it says Add RSS Feed.
| | 09:51 | If I take my cursor away and click over here,
you can see how nicely centered that is and everything.
| | 09:56 | But now I'm going to set its color to white,
and we won't be able to see it very well anymore.
| | 10:02 | Here's the Text Color and make it White Color.
| | 10:08 | And so now it becomes much more
difficult to see, but it is there.
| | 10:12 | I'm going make sure this is properly centered.
| | 10:16 | That is just necessary because this is
actually a darker background than what it looks like.
| | 10:21 | So, we going to grab one more Text Label here,
and we're going line this up so that it's
| | 10:26 | centered under this other Text
Label that we have got there.
| | 10:29 | And I'm going to grab the ends of
it and drag them out to the sides.
| | 10:34 | This one is going to say, "Enter a
URL," and that will serve as a prompt.
| | 10:38 | It is also going to serve as a status line,
and this is going to be System Font and its
| | 10:43 | Size is going to be 12.
| | 10:48 | And again, we're going to set the color
to white so it shows up really nicely.
| | 10:53 | So, I can either select it down here or I
can select it from the Recently Used Colors.
| | 10:57 | So, that will be White also, and we'll
have trouble seeing it until we run it.
| | 11:01 | Now, we're going to grab a Text Field, and we're
going to drop that in right under the Text Label.
| | 11:07 | You see these little guides. They really
help with this a lot, for lining things up.
| | 11:14 | Our Text Field is going to start with
http://. And I'm not using that as a placeholder text
| | 11:20 | because we don't want it to
disappear when we start to type in there.
| | 11:23 | We want to be able to actually use that as
a prefix or select it and get rid of it if
| | 11:26 | we want to. So that works really well.
| | 11:28 | We're going to set some options down here:
Capitalization, None, Correction, No, the
| | 11:35 | Keyboard will be the URL keyboard,
| | 11:37 | its Appearance is the Default,
and the Return key will say Done.
| | 11:44 | And we're not going to change the Text Color
on this one because it's going to show up
| | 11:47 | like this with the white background, and it will
actually look really nice with that textured background.
| | 11:52 | Now, we're going to add a couple of buttons,
round rectangle buttons. And I'm going to
| | 11:58 | start with them over here to the left.
| | 12:01 | And I'm just going to kind of put them
close to each other like that, and then I'm going
| | 12:05 | select both of them and drag them to the center so
that they're centered but they're spaced just nicely.
| | 12:10 | And the one on the left is going to say Cancel,
and the one on the right is going to say Add.
| | 12:19 | Now, I'm just going to scroll a little bit
so we can see our Controller Delegate--it's
| | 12:25 | really difficult with the amount of space
I have here on the screen--because we want
| | 12:28 | to be able to hook up a
few of these things here.
| | 12:31 | Firstly, I'm going to control drag from the text
field all the way down here to our controller,
| | 12:37 | and I'm going to select Delegate.
| | 12:41 | All right, and that allows this Text Field to
delegate its protocol to our AddViewController,
| | 12:49 | and I'm going to press Command+S here.
That allows us to do this thing down here.
| | 12:54 | Text Field should return, that allows us to get this
message when the Done key is pressed on the keyboard.
| | 13:01 | And the Cancel and Add buttons, again,
I'm going to control drag all the way down here.
| | 13:07 | So, that's the Cancel action.
| | 13:13 | It looks like it worked.
Let's just do it this way.
| | 13:17 | Check and make sure that worked.
| | 13:19 | Yes, there is our Cancel action, that's
great. And our Add action, I can just drag right
| | 13:26 | from there to our Add button,
and it's Touch Up Inside.
| | 13:30 | You'll notice that the Default Action is
Touch Up Inside, so you can drag it this way.
| | 13:34 | It's just a little bit more
complicated to do it that way.
| | 13:36 | If I didn't do that, I can drag it from here,
and I have to drag it all the way down here
| | 13:43 | to the Add action there.
| | 13:46 | And then when I select this, we can see
that we get that Touch Up Inside action.
| | 13:52 | So, if we select the Controller and our
Connections Inspector, we can see all of these connections.
| | 14:00 | Our statusLabel will go
here to our white label there.
| | 14:05 | Our URL Text Field will come here.
| | 14:08 | And now, it looks like we've hooked
up everything that we need to hook up.
| | 14:11 | So, I'm going to press Command+S
again to make sure this is saved.
| | 14:16 | And now let's go ahead and
run it on the iPhone Simulator.
| | 14:19 | I'm going to select our Run button here.
| | 14:22 | Now, we're just going to press this
Plus button, and there's our modal dialog.
| | 14:29 | And you'll notice that it gave the focus to
our Text Field, and so here's our little keyboard.
| | 14:34 | Our label RSS Feed or Enter URL in
white, those are very nicely visible.
| | 14:39 | If I press the Cancel button, it dismisses it.
| | 14:42 | If I press the Add button, of course
dismissing it is all that anything it does. And if I
| | 14:47 | type in a URL and press the Done button,
you see that that dismisses it as well.
| | 14:53 | We now have a working storyboard interface
that we can use for the modal dialog box.
| | 14:58 | The next step will be to implement the
methods in the AddViewController so we can start
| | 15:02 | adding feeds to our database.
| | 15:07 |
| | Collapse this transcript |
| Finding a feed link in a web page| 00:00 | Now that we have the Add View constructed, our
first task is to get the URL for the RSS feed.
| | 00:05 | Many website support the RSS autodiscovery
specification using a link tag to point to
| | 00:11 | the URL for an RSS feed.
| | 00:14 | Our Add View controller will have support
for either the URL of an RSS feed or the URL
| | 00:18 | of a web page that
supports RSS feed autodiscovery.
| | 00:22 | When the user enters the URL,
it could be either a web page or a feed.
| | 00:26 | So the first thing we need to do is
use NSURL Connection to fetch the URL.
| | 00:31 | Then we inspect the MIME type to find
out if it's a web page or an RSS feed.
| | 00:36 | If we have a web page, we need to look in
the header section of the web page to see
| | 00:39 | if there's a link tag pointing to an RSS feed,
and then go fetch that URL and treat it as the feed.
| | 00:45 | For example, Ars Technica here, if I press
Option+Command+U--this is Chrome so that will
| | 00:51 | show the source for the web page--
| | 00:54 | you notice down here we have this link tag
right there: relationship, alternate, type,
| | 00:59 | application rss+xml, and
then the URL of the RSS feed.
| | 01:03 | So, that's the RSS autodiscovery right there.
| | 01:07 | On the other hand, Macworld's web page,
if I press Option+Command+U to view the source,
| | 01:13 | you notice there are a lot of link tags here,
but those are all relationship style sheet.
| | 01:17 | There is no RSS autodiscovering in this page.
| | 01:20 | If I just search for the letters RSS on
the page, all we find is this link to another
| | 01:27 | page with the RSS feeds on it.
| | 01:29 | So macworld.com is not
supporting the RSS autodiscovery.
| | 01:34 | Our implementation here in BWRSS is a very simple
implementation that works in most circumstances.
| | 01:39 | It does not support multiple RSS feeds per
page, nor does it support relative links.
| | 01:45 | You can see the full specification for the RSS
autodiscovery here at the RSS Advisory Board website.
| | 01:52 | Now, let's set up our Add View
Controller to discover and fetch an RSS feed.
| | 01:56 | So, we're going to start with our addView-02,
and I'm going to use the addView-02-done here.
| | 02:04 | You can start with the code that you wrote
in the last movie if you're following along.
| | 02:07 | I want to make a working copy of this,
and I'm going to rename it to addView-03.
| | 02:12 | I'm going to go ahead and open that in Xcode
by double-clicking on the Xcode project file.
| | 02:18 | I'm going to go ahead and hide this right-hand
pane so we have a little more room on the screen here.
| | 02:24 | I'm going to come back out to the Finder,
and I'm going to open this methods.txt file,
| | 02:29 | addView-03-methods.txt, in my text editor.
And coming up here to the top of the file, I'm
| | 02:35 | just going to grab the Error handling and
Utilities, all the way down here to just before
| | 02:42 | the RSS Feed Management.
| | 02:43 | I'm going to press Command+C on my keyboard
and paste this into the bottom of this file,
| | 02:49 | just above the @end there. There we have it.
| | 02:53 | The error routines are very straight forward.
| | 02:56 | And the utilities, we have
the statusMessage. This is new.
| | 02:59 | What it does is it takes a variadic
formatted argument or argument list, and it formats
| | 03:07 | it and sets these statusLabel.
| | 03:09 | The best way to explain this
is to show an example of it.
| | 03:12 | If I come back up here to our button
actions, and the addAction, I'm just going
| | 03:16 | to comment out this dismissed view controller,
and I'm going to say self statusMessage and
| | 03:25 | Add button pressed.
| | 03:26 | And now, when I run this in the simulator--
select the iPhone Simulator and press Run--
| | 03:33 | and I'll bring up our Add Feed view--
| | 03:38 | And you notice when I press the Add button, you
watch the statusMessage here on this text label.
| | 03:43 | I press Add, and you see it says Add
button pressed. Press Cancel, it goes way.
| | 03:49 | If I do it again, you'll also notice, if I
press this Done button down here, we get the
| | 03:52 | same thing, Add button pressed. And that's
because we have here textFieldShouldReturn.
| | 03:58 | This class is set up as the delegate for the
textField and so anytime that button is pressed,
| | 04:03 | it just calls addAction and then
we get our statusMessage again.
| | 04:07 | So that's what
statusMessage does. It's variadic.
| | 04:10 | It allows for parameters, and we'll see a lot
more examples of it as we go through this code.
| | 04:16 | Now, let's fetch the web
object with NSURL connection.
| | 04:18 | We're going to come back out here to where text
file. We're just going to select everything
| | 04:23 | else here, all the way down to the end.
| | 04:31 | You notice we have our button actions as well.
| | 04:35 | Those are going to replace the ones.
We'll move those ones we copied them in.
| | 04:38 | So, let's go back into Xcode and come down here to
the bottom, and I'm going to paste those in here.
| | 04:45 | And you notice that these button actions,
they get the error because they're duplicate.
| | 04:49 | I'm just going to cut with Command+X and come back
up here to our button actions and paste them in there.
| | 04:59 | Press Command+S to save.
| | 05:02 | So what we have here now, you'll notice in
Add action, we're calling getRSSFeed, and
| | 05:11 | we find that down here in RSS Feed Management.
getRSSFeed, it first fixes up the URL to make
| | 05:16 | sure that it has either a prefix of HTTP or
HTTPS. And if not, it adds that by appending
| | 05:23 | the string. And then it calls fetchURL, and
fetch URL actually sets up the URL request
| | 05:28 | with NSURLRequest. Aetting the delegate to
self, and it calls that and passes off all
| | 05:34 | of our execution to the NSURL delegate methods.
| | 05:38 | Our NSURL delegate methods
are very straightforward.
| | 05:42 | You notice a lot of these methods are
checking the BWRSSState to see if we're in the Parse
| | 05:46 | Header or State Discovery mode.
| | 05:48 | If we're in the State Discovery mode, then
we're actually looking through an HTML file
| | 05:53 | to find that RSS Discovery link tag, and
if we're in the Parse Header mode then we're
| | 05:58 | actually going through an XML
file and reading the RSS data.
| | 06:02 | But the rest of this we've mostly seen before.
When we received before when we receive data,
| | 06:05 | we append it into our mutable data structure.
And on DidFinishLoading, depending on if we're
| | 06:12 | on the discovery mode or the parse
mode, we call the appropriate function.
| | 06:15 | Now at this point, we're not
ready to start parsing things yet,
| | 06:19 | so we're going to go ahead and comment this
out and comment this out. And I want to put
| | 06:24 | in a little status message here so that we
can see that we are actually getting the data
| | 06:29 | from the feed, so I'm going to say self
statusMessage, and I'm going to say Have, %d bytes of data,
| | 06:39 | and we're going to get
that from our xmlData.length.
| | 06:42 | So, we save that with Command+S.
| | 06:47 | Make sure the iPhone Simulator is selected,
and I'm going to press the Run button.
| | 06:52 | Looks like I didn't stop it last time.
That's okay. I just press Stop, and I'm going to
| | 06:57 | bring up our Add View Controller and type
in cnn.com this time, because I know that's
| | 07:03 | a good amount of data.
| | 07:04 | I'm going to press the Add button, and
here we have it, Have 134,791 bytes of data.
| | 07:11 | Of course, your result will be somewhat
different than that because I'm sure they change their
| | 07:16 | website constantly.
| | 07:18 | So at this point, we have fetched the web
object, and you can see that result there
| | 07:22 | and in DidReceiveResponse,
| | 07:24 | we can check the mime type to find
out if it's a web page or an RSS feed.
| | 07:28 | If it's a web page, we need to check for a
link tag that will point us to the actual
| | 07:32 | RSS feed, and this is done in the
rssLinkFromHTML code. Stop our simulator.
| | 07:43 | There's the rssLinkFromHTML. And you'll
notice that it uses the NSScanner object to scan
| | 07:51 | through and look for a link, the relationship,
and the type, and if it has the correct mime type.
| | 08:00 | And so, if we come back up here to
connectionDidFinishLoading--and we will uncomment this so that now we're
| | 08:08 | calling findFeedURL--and then, down here in
the StateParseHeader, we can put in a new
| | 08:15 | statusMessage, and we'll go ahead and run
this again in the Simulator. Type in cnn.com
| | 08:26 | and press Add, and now we have an RSS feed.
| | 08:30 | So, now we have found and
retrieved the RSS feed from a web server.
| | 08:34 | This involved fetching the provided URL and
determining if it was a web page or an RSS
| | 08:39 | feed, and for the RSS feed we simply fetch
the feed for parsing, and for web pages we
| | 08:44 | search for the first link tag that links to an
RSS feed and fetch that for parsing as a feed.
| | 08:50 | Now we can go ahead and parse the feed using
NSXMLParser, and we'll do that in the next movie.
| | 08:55 |
| | Collapse this transcript |
| Parsing the feed with NSXMLParser| 00:00 | Now that we have an RSS feed, we need to run
NSXMLParser to get the title and description of the feed.
| | 00:07 | These will be stored in the database and
used for displaying the main feed table view.
| | 00:11 | In this movie, we'll
concentrate on parsing the feed.
| | 00:14 | We'll start by making a working copy of
BWRSS-addView-03, and I'm going to use the -done version.
| | 00:21 | You can use the version that you worked on
in the last movie if you're following along.
| | 00:24 | I'm going to rename this as addView-04 and
open it in Xcode by double-clicking on the
| | 00:30 | Xcode project file.
| | 00:32 | I'm going to come back out to the Finder now,
and I'm going to grab this methods.txt file,
| | 00:38 | and I'm just going to select all of this.
| | 00:40 | This is just all of the parser code.
And I'll select all with Command+A and Command+C to
| | 00:46 | Copy, and come back here into Xcode and drop
it in here at the bottom before the end tag.
| | 00:54 | Command+S to save, and we're going to back
up here to connectionDidFinishLoading, and
| | 01:01 | where we have this self parseRSSHeader,
| | 01:04 | I'm going to just uncomment that. And then
we're going to come up to haveFeed, which is
| | 01:11 | in our RSS Feed Management. And where
we call the delegate haveAddViewRecord--
| | 01:17 | we haven't implemented that delegate code yet--
| | 01:19 | I'm going to comment that out.
| | 01:21 | I'm also going to comment out this
dismissViewController so that we can see this result. And I'm going
| | 01:27 | to put in a status message, self statusMessage:@*
Have feed, and it's name, which will be from feedRecord
| | 01:36 | kTitleKey, and Command+S to save.
| | 01:41 | Now, let's go ahead and run it in the
Simulator. And we'll add that CNN feed again. And we
| | 01:50 | have the feed, and you can see it says,
"CNN.com Top Stories," and that means that we are
| | 01:55 | successfully parsing the feed
because that's the title of the feed.
| | 01:59 | So, I'll press Cancel just to dismiss our
view controller and come back over here to
| | 02:03 | Xcode and press Command+. to stop the simulator.
| | 02:08 | Let's take a look at the parsing process.
| | 02:10 | Again, this is very simple and straightforward.
| | 02:14 | We call the parser, and the parser starts its
process and uses these callback methods, these
| | 02:19 | delegate methods, in order to allow us to
control the different parts of the parsing process,
| | 02:24 | and to extract our data where we need to.
| | 02:27 | DidStartDocument allows us to set up our feed
record to MutableDictionary. didStartElement
| | 02:32 | gets called at the beginning of each element
and allows us to finish up the element before,
| | 02:38 | and to store some data. And you'll notice
that there is something here that's not working
| | 02:42 | in iOS 6, and I'll get back to that in a moment.
| | 02:48 | Parser didEndElement gets called at the end
of each element. And again, it allows us to
| | 02:53 | process the data from that element.
parser foundCharacters for elements that are containers,
| | 02:59 | this allows us to accumulate date within those
containers. And parseErrorOccurred gets called
| | 03:05 | whenever there is an error, and when Abort is
called, although that's not working in iOS 6.
| | 03:11 | So, let's come back up here to didEndElement,
and you see this if (haveTitle && haveDescription),
| | 03:17 | didFinishParsing = TRUE, and
then a call to parser abortParsing.
| | 03:21 | Now, what's supposed to happen when you
call parser abortParsing is you're supposed to
| | 03:26 | get an error message sent to
parseErrorOccurred saying that the parsing had been aborted.
| | 03:33 | And this used to work fine and previous
versions of this code used this because we're
| | 03:38 | just looking for a few items
in the top part of the XML file;
| | 03:41 | we don't need to parse an entire 100k XML
file just to find the first few entries in
| | 03:45 | the file, and that's what
this is supposed to before.
| | 03:48 | So, when we get our title and our description,
we want to just abort at that point and stop
| | 03:54 | wasting everybody's time and energy.
And this used to work in previous versions. Starting
| | 03:58 | in beta 4 of iOS 6,
| | 04:00 | this did not work anymore. And I was finding
that existing versions of my RSS reader had
| | 04:06 | just stopped working on iOS 6, so I hunted
around, I found the problem, and I found the
| | 04:09 | workaround, and I sent a bug report to
Apple and they have acknowledged that. But it has
| | 04:13 | not yet been fixed, and we're now on
iOS 6.1 as in beta as I'm recording this, and this
| | 04:19 | particular bug still hasn't been fixed.
| | 04:21 | So, I've just taken it out.
| | 04:23 | We're not doing that anymore. And we're
parsing the entire file, although you'll notice that
| | 04:27 | that I set a flag that says didFinishParsing,
and I have this in a lot of these methods,
| | 04:32 | if (didFinishParsing), and I just return, so that I'm
still not wasting anymore energy than I need to be.
| | 04:39 | So, we're now successfully parsing the
header of the RSS feed, and as you can see, this
| | 04:44 | is a lot less complicated than the parser
in the item view controller because here,
| | 04:48 | really, we're only
looking for a couple of things.
| | 04:50 | The delegation pattern used by NSXML parser
and also used by a lot of the Cocoa framework
| | 04:55 | is flexible enough that we can really just
pay attention to the parts that matter, giving
| | 04:59 | us the simplicity where we need it or
the complexity where we might need that.
| | 05:04 |
| | Collapse this transcript |
| Delegating back to the parent view| 00:00 | Now that we're parsing the feed header, we need to send
data back to the FeedsTableViewController for processing.
| | 00:06 | Remember, the
AddViewController object is a modal dialog.
| | 00:09 | What we want to do is little
processing as possible there.
| | 00:11 | We created a small delegate protocol for
communicating with the FeedsTableViewController, and now
| | 00:16 | we're going to use it.
| | 00:17 | So, let's start by making a working copy of
addView-04, and I'm going to use my -done version.
| | 00:24 | You can use the one that you worked on in the last
movie if you like, if you've been following along.
| | 00:29 | And I'm just going to call this addView-05, and
I'm going to open it in Xcode by double-clicking
| | 00:35 | on the Xcode project file.
| | 00:37 | Now, if we notice in our addView.h file, we have this
protocol that we define, this RSSAddViewControllerDelegate
| | 00:45 | protocol. And it has three methods in it,
and those methods are actually going to be
| | 00:50 | run in the FeedsTableViewController.
| | 00:52 | So, the FeedsTableViewController is going to
be the delegate for this delegate protocol.
| | 00:58 | And you'll notice right here we have this
property which is called delegate, which is
| | 01:01 | of type id with this delegate protocol.
| | 01:05 | And you'll notice, back here in our haveFeed
method, we actually call haveAddViewRecord,
| | 01:14 | which is one of our delegate methods,
haveAddViewRecord on that delegate.
| | 01:19 | So, I'm going to uncomment this, and I'm going
to uncomment this to dismiss the view controller.
| | 01:24 | And now we need to set this delegate property here,
and that gets set from the FeedsTableViewController.
| | 01:30 | Now, in the FeedsTableViewController,
this is going to be a set in our segue, because
| | 01:37 | our segue is what happens when we press on
that Plus button. Then we get the segue to
| | 01:41 | the AddViewModelController and the model
controller, it slides up the screen with that animation.
| | 01:47 | It covers the entire screen.
| | 01:49 | And before we can start handling this in
the segue, we need to know about it, so we're
| | 01:53 | going to come in here to our controller.h,
and I'm going to import our AddViewController
| | 01:59 | header. And I'm going to come down here to
our interface which defines the class type
| | 02:05 | and the parent class type.
| | 02:06 | I'm going to declare this protocol type as well.
| | 02:09 | So, this is our RSSAddViewControllerDelegate.
| | 02:11 | So, I'll save here, and I'm going to come
back over here to the end file, and now we
| | 02:18 | can handle this other segue.
| | 02:20 | Now, if you remember, we come over here to
the iPhone storyboard--and I'm just going
| | 02:25 | to display this right side real quick.
| | 02:31 | So, this is the segue here, and it has the
segue identifier, SegueToAddView, so I'm just
| | 02:38 | going to copy that so that I can paste it
in and know that I have spelled it correctly.
| | 02:43 | Over here, I'm going to say else if (segue.
identifier isEqualToString:@"SegueToAddView"), and then
| | 02:54 | I'm going to declare a pointer to the BWRSS
AddViewController class. And that's our destinationViewController
| | 03:04 | in the segue, so I can just
say destinationViewController.
| | 03:09 | Now that we have a pointer to that destinationViewController,
I can set its delegate to this FeedsTableViewController.
| | 03:18 | Now, when the AddViewController calls
something on its delegate, it's going to be calling
| | 03:23 | it on this controller, the
BWRSS FeedsTableViewController.
| | 03:27 | This controller is
declared to handle that protocol.
| | 03:32 | You notice up here it says that our
protocol is not implemented yet.
| | 03:36 | That's because we haven't yet added those
methods. And so I'm going to add those methods
| | 03:42 | right here before the database methods.
And we'll come out here to the Finder, and we'll
| | 03:45 | open our 05-methods.txt file, and there they
are. I'm going to select all and copy and come
| | 03:54 | back into Xcode and paste them in
right there. Command+S to save.
| | 03:59 | So now, our AddViewController, when it has a
feed, it's going to call this haveAddViewRecord
| | 04:04 | with the feedRecord.
| | 04:06 | Here in our FeedsTableViewController,
here it is, and it's going to get that record,
| | 04:10 | it's going to sign it to this new feed
variable. And so we need to do one more thing.
| | 04:14 | When that gets assigned, we need to come
back up here in our view stuff, and I'm going to
| | 04:20 | come down here and I'm going to declare a
viewDidAppear, and if (newFeed), (self loadNewFeed).
| | 04:33 | If we come down here and we see what
loadNewFeed does, it goes ahead and either inserts it
| | 04:39 | in the database or updates it in the
database, and it does this nice animation reveal.
| | 04:45 | We'll see what that looks like right now.
| | 04:47 | We're going to go ahead and
run this in the simulator.
| | 04:49 | So now, let's go ahead and add that CNN feed--
CNN.com--and I'll press Add, and you see it
| | 04:58 | has that nice little horizontal reveal.
| | 05:01 | You notice it's using this UITableViewRowAnimationLeft,
and it's using the same one here when it's updated.
| | 05:08 | So, if I go ahead and put in that same URL,
you'll notice that it comes in here and it
| | 05:14 | just rotates it into place like that.
| | 05:17 | So, if we come back up here to our
AddViewControllerDelegate method, you'll notice here that there is
| | 05:22 | URL errors and there is RSS errors.
| | 05:25 | So, if I come back here to the simulator--
and let's just put in something that doesn't
| | 05:30 | have a feed, like for instance music.bw.org
I know that one doesn't have a feed--and it
| | 05:36 | says, RSS Error did not find a feed.
| | 05:38 | So, that's this haveAddViewRecord message.
And for an error, if I put in a URL that just
| | 05:44 | doesn't exist, like x.y, and click Add, it says,
URL Error, a server with a specified host
| | 05:50 | name could not be found.
| | 05:51 | So, those are these two messages here.
| | 05:54 | So now the feed is being properly added
and updated, and it's all working quite well.
| | 05:58 | The delegate protocol that we created is
very easy to use, and it's also working well.
| | 06:03 | This allows the modal view to do its job
while passing its results back to the main
| | 06:07 | view so that it can do its job.
It's all very neat and tidy.
| | 06:12 |
| | Collapse this transcript |
| Deleting feeds| 00:00 | Now that we can add feeds,
we should be able to delete feeds, too.
| | 00:04 | Cocoa Touch has a neat UI for this.
| | 00:06 | You can press an Edit button which will reveal
special red delete icons, or you can swipe to delete.
| | 00:11 | This interface is easy to implement, and because
it's so common, it will be familiar to the user.
| | 00:16 | So, we'll start by making a working copy of
BWRSS-addView-05, and we'll rename it as 06.
| | 00:22 | So, I'm going to use the done version here.
And you can use the done version or you can
| | 00:28 | use the version that you created in the
last movie if you've been following along.
| | 00:33 | And I'm going to open this in Xcode by
double-clicking on the Xcode project file. And we're going
| | 00:39 | to come back out here to the Finder and I'm
going to open this 06-methods.txt file.
| | 00:47 | And it just has this one method in it, and I'm going to copy
that and come over in to Xcode and we'll paste it in.
| | 00:54 | We're going to put this right at
the end of the tableView section.
| | 01:00 | We'll go ahead and delete all of this,
and we'll just paste it in right there.
| | 01:05 | This method is called tableView commitEditingStyle
forRowAtIndexPath, and it passes this editingStyle,
| | 01:12 | and if the editingStyle is the
ViewCellEditingStyleDelete, then we go ahead and implement the delete.
| | 01:19 | So, this is really very simple, and if we go
ahead and run this in the iPhone Simulator,
| | 01:26 | we see we have the CNN.com Top
Stories that we added in the last movie.
| | 01:30 | I'm just going to press this Edit button and
you'll notice there I get these little red Delete icons.
| | 01:34 | If I select the one next to CNN.com Top Stories,
I get a Delete button, and I can press that,
| | 01:40 | and it deletes it, and I can press Done, and it's
gone. And that's all what we've just implemented here.
| | 01:46 | It actually deleted it from the database
and it loaded the FeedIDs again and displayed
| | 01:51 | them again, and it calls this
deleteRowsAtIndexPath with RowAnimation to animate the delete, and
| | 01:58 | we're using this fade method.
| | 02:00 | We can change this, and there's a number
of different methods available. We could use
| | 02:03 | one called Left. I need to stop it and
run it again. And I'll go ahead and I'll add
| | 02:11 | CNN again, and then we delete it.
| | 02:15 | We will go ahead and this time we'll do the
swipe gesture for delete, and when we do the
| | 02:18 | swipe gesture, we get the same little Delete
button, and you'll notice this time it's swiped
| | 02:22 | off to the left for the delete.
| | 02:24 | So, we have left, we have right, we have top,
we have middle, and we have automatic, as well
| | 02:30 | as the fade that we started with.
| | 02:33 | The middle one is a little bit interesting.
| | 02:34 | It might not show up well on the screen,
but if you try this on your computer--
| | 02:38 | I'm going to go ahead and run this again,
and we'll add that same feed again, and we'll
| | 02:46 | go ahead and delete it--
| | 02:47 | you see that it folds over when I
press this, and it deletes it that way.
| | 02:52 | So, I'm just going to return this to the fade
version, and we'll save that I'm going to press Command+.
| | 02:57 | to stop it running on the simulator.
| | 03:00 | So, that's really all there is to it.
| | 03:02 | Cocoa Touch makes this really easy to
implement and all the heavy lifting is done for you.
| | 03:06 | All you need to do is implement the
proper delegate method, and it works just fine.
| | 03:11 |
| | Collapse this transcript |
|
|
6. The Web View ControllerCreating the web view class| 00:01 | In order to display pages in the WebView,
we need to create a class for the WebView
| | 00:05 | delegate methods, and we need to
create the view in the storyboard.
| | 00:09 | So, we'll start by making a working copy of
the BWRSS-webView-start. That's in chapter
| | 00:15 | 6 in our exercise files. And I'm just going
to drag it here with the Option key pressed
| | 00:22 | and rename it to BWRSS-webView-01.
And we'll go ahead and open this in Xcode by double-
| | 00:30 | clicking on the Xcode project file. And I'm just
going to do a couple things here to get situated.
| | 00:37 | I'm going to make sure there
are iPhone Simulator selected.
| | 00:40 | I'm going to open a couple of these disclosure
triangles, and we're going to start by creating a new class.
| | 00:47 | So, I can either select File > New > File
here, or I can just right-click on the BWRSS
| | 00:55 | group and say New File.
| | 00:57 | This is going to be a
Cocoa Touch Objective-C class.
| | 01:01 | Press Next. And it's going to be called
BWRSSWebViewController. And it's a subclass of ViewController, so
| | 01:10 | if you have TableViewController or something
else selected, you want to make it a subclass
| | 01:14 | of ViewController.
And leave both of these unchecked.
| | 01:17 | It's not targeted for iPad, and it's not
using a NIB file, so we say Next. And it will be
| | 01:24 | in the BWRSS group and with the BWRSS Target
checked, and this is the correct folder for it.
| | 01:32 | So, that's all good.
We select Create, and there it is.
| | 01:36 | You notice it drops it in down here at
the end because it's kind of alphabetical.
| | 01:41 | I'm just going to move it up in there.
And I'm going to open up the .h file and come
| | 01:46 | back out here to our finder, and you see
here we have the 01-header.text file there.
| | 01:54 | I'm going to open that up in my text editor.
| | 01:57 | I'm just going to select all of that and
come back in here to Xcode, and if I select this
| | 02:03 | and replace this line with all of
that, it actually comes out right.
| | 02:07 | You'll notice--I'm going to
minimize this Utilities bar over here.
| | 02:11 | You'll notice we're implementing the
WebViewDelegate protocol, and we have an IBAction for something
| | 02:19 | called an actionButton. We'll see what that
is in a little bit here. And we have IBOutlets
| | 02:24 | for the webView and a backButton and a
forwardButton, and a property for feedItem that gets passed
| | 02:31 | from the Items View
Controller when this view is launched.
| | 02:33 | So, I'm going to press Command+S here to safe.
| | 02:35 | I'm going to move over here to the .m file.
And all we really need to do in here, and
| | 02:40 | you see because this is just based on the
View Controller, it's pretty lightweight here.
| | 02:45 | It doesn't have a lot of stuff in the template.
| | 02:47 | Also, you notice this little exclamation
point. It says, "Incomplete implementation," That's
| | 02:51 | because in our .h file, we have this
IBAction method, and it's not defined in here,
| | 02:57 | so we're going to go ahead and define that.
| | 02:58 | We're just going to put that down here.
And it looks like that, and we're just going to
| | 03:05 | put a little NSLog in here for now.
| | 03:09 | And what's that's going to do is when this
button is pressed, it's going to display in
| | 03:15 | the log the name or signature of it's
function, which will look kind of like that, and the
| | 03:21 | URL that's been passed from the Items view,
and that just will let us know that this button
| | 03:26 | is working when we press this particular button.
| | 03:29 | So, I'm going to press Command+S to save this,
and we're going to come over here now to our
| | 03:32 | iPhone storyboard--that's this one here--
and we're going to create our WebView.
| | 03:39 | Now, I'm going to bring
this Utilities panel back out.
| | 03:44 | I'm going to scroll back over.
| | 03:46 | This is a little bit complicated to
do with the limited real estate here.
| | 03:49 | Zoom out, I'm going to grab us a new View
Controller, and that's going to go over here in some place.
| | 03:56 | Try and align that up a little bit. And it's
going to insist that I scroll in to do this next part.
| | 04:03 | I'm going to create a segue.
| | 04:05 | Now, when I create a segue, I'm going to
hold down the Control key and I'm going to drag
| | 04:09 | from this prototype cell,
and that will create the segue.
| | 04:13 | I'm going to select Push.
| | 04:14 | Now you'll notice that one of the things that
it does here is it adds that disclosure arrow
| | 04:19 | back into the prototype cell, and it sets this
accessory property here to Disclosure Indicator,
| | 04:25 | so I have to set it back
to None if I don't want that.
| | 04:28 | So now, we have this new view.
| | 04:31 | And in this new view, we want to have a
WebView and we want to have a toolbar.
| | 04:35 | So, we're going to start with the toolbar
because it actually make sense, because then
| | 04:40 | the WebView will snap into the right place.
| | 04:42 | So, I'm going to grab this toolbar here
and I'm just going to drop it into the bottom,
| | 04:46 | and I'm going to grab a Web
View now, and that's in Data Views.
| | 04:51 | And you'll notice that it just kind of
fills that remaining space just nicely.
| | 04:56 | And then I'm going to come down here and I'm
going to set the class of the View Controller.
| | 05:02 | So, I'm selecting the identity inspector, and I'm
going to select our Web View Controller for that.
| | 05:09 | Now, whenever I hover over that,
it says Web View Controller.
| | 05:12 | I'm going to make sure that our segue is
named properly, so I'm going to select the segue
| | 05:17 | here and come back over to the Attributes Inspector.
And I'm going to type in SegueToWebView and press Enter.
| | 05:27 | Now, our segue is named properly.
| | 05:29 | Now, I want to add some buttons to our
button bar down here, but before I do that I want
| | 05:33 | to show you the buttons.
| | 05:34 | A couple of these buttons I've had to
create them myself, and I used PhotoShop.
| | 05:38 | You can use whatever application you want to.
| | 05:40 | I'm going to press Command+S to save
here and come back out to the Finder.
| | 05:43 | And here, in this Assets folder, you'll see
four PNG files. And if I bring these up in
| | 05:50 | Preview, see there's one called back-arrow.png, and
it looks like that, and there's back-arrow@2x.png,
| | 05:58 | and it's the same thing, just a little
larger, forward-arrow and forward-arrow@2x.
| | 06:06 | The thing that these images is these are
going to go on a toolbar, and the way that these
| | 06:11 | work is that the only thing that's actually used in
the toolbar is the transparency mask of the PNG file.
| | 06:18 | So, what color it is, what image is in there,
any of that is completely discarded, and all
| | 06:24 | that's used is the transparency mask.
| | 06:26 | So, I just make them black on
transparent, and that works out just fine.
| | 06:31 | So, I'm going to take these images and I'm
going to drag them into our Supporting Files
| | 06:36 | folder here, and you'll notice
that we get this nice little dialog box.
| | 06:41 | I'll make sure that copy items is checked.
Create groups, there aren't any groups so
| | 06:45 | that's fine, and Add to target this BWRSS.
| | 06:48 | So, I select Finish, and
there is our four graphics there.
| | 06:54 | Now, when we come over here to our toolbar--
and I'm going to just select this item.
| | 06:59 | You notice the toolbar starts with one item on it.
And I'm going to say that it's plain.
| | 07:04 | It doesn't have a border, and
I'm going to select an image for it.
| | 07:06 | You'll notice that our graphics are here.
| | 07:09 | I don't want to select one of the
@2X ones. That happens automatically.
| | 07:13 | If I select the back-arrow, for the retina
displays, the @2x, the larger-size one, will
| | 07:18 | automatically be used.
| | 07:20 | So now, we have a back-arrow.png there,
and you'll notice that it displays just as you
| | 07:25 | would expect it to.
| | 07:26 | It's just using the transparency mask
to find the boundaries of the image.
| | 07:30 | So, that's pretty nice, and that works great.
| | 07:32 | I'm going to come back over here to Windows &
Bars, and I'm going to grab the Flexible Space.
| | 07:38 | What we're going to do is we're going to
put five buttons out here and flexible space in
| | 07:42 | between all of them.
| | 07:42 | So, that's four flexible spaces.
| | 07:44 | I'm just going to drop all of those in
place right now because it's convenient and easy
| | 07:48 | to do it this way.
| | 07:50 | And then I'm going to come back up here
and I'm going to get Bar button Item, and I'm
| | 07:53 | going to get four more of those.
| | 07:55 | We've got one already. And I'm going to drop
all of those in between the flexible spaces.
| | 08:00 | So, that lays it out just perfectly, like that.
| | 08:03 | And then for each of these,
I'm going to make them Plain.
| | 08:06 | And this first one, this will be the right-arrow
image, the forward-arrow image, and this next one, Plain.
| | 08:13 | This next one is going to be the Refresh image.
| | 08:18 | That's one of the stock ones.
| | 08:19 | This one is going to be the stop image, so Stop.
| | 08:24 | That's got that X to it. And you see how
they just resize and that flexible space in between
| | 08:28 | just works to hold them all
exactly the same space apart.
| | 08:32 | It works really nicely.
| | 08:33 | And this last one is one that is called Action,
and that's the one that's going to get that action method.
| | 08:40 | That's for, when you press that it will load the
web page up in Safari. It will move away from
| | 08:45 | this app and open it up in Safari for those
times when you want to load a web page in Safari.
| | 08:49 | Of course, these flexible spaces won't
actually show up on the app, but it's all going to
| | 08:54 | look really nice like that.
| | 08:56 | And now we're going to hook things up.
| | 08:58 | We can come over here to our Web View Controller,
and we're going to select the Connection Inspector
| | 09:04 | here, and you'll notice that
we have this Web View Property.
| | 09:07 | Remember in our WebViewController.h, we had
this WebView IBOutlet, and that's what that is.
| | 09:15 | And so we're going to grab that,
and we're going to hook it up to our WebView.
| | 09:18 | And these Back button and Forward button items,
these are the IBOutlets that we declared for
| | 09:22 | those. And that actually allows us, not so
much to intercept these, but to refer to them
| | 09:29 | so that we can gray them out and
inactivate them when they're not valid.
| | 09:33 | And we'll see how to do
that later in this chapter.
| | 09:37 | This Action button,
that's what we can intercept that action.
| | 09:41 | And then we're going to select the WebView,
and the WebView itself has some actions that
| | 09:46 | we can hook up to these buttons so that all of
these buttons automatically work with the WebView.
| | 09:50 | We've got the Go Back button,
which go to our left arrow;
| | 09:54 | and our Go Forward button,
which will go to our forward arrow;
| | 09:57 | reload, which will go to the Refresh button; and
Stop Loading, which will go to the Stop button.
| | 10:04 | So that's all based on the WebView itself.
| | 10:06 | Okay, so I can save this.
| | 10:08 | And in order for this to work, we need to
go back here into the ItemsViewController
| | 10:13 | and hook it up to the segue.
| | 10:15 | So, in order to use this class from
here, we need to import its header file.
| | 10:21 | So, I'm going to come back out here to the Finder now, and
I'm going to grab this ItemsTableViewController-01methods.txt
| | 10:28 | file, and that has our segue
delegate for calling up our WebView.
| | 10:34 | So, I'm going to come back into Xcode, and
I'll just drop that in right here before the
| | 10:42 | Table view data source part.
| | 10:45 | And it looks for the Segue identifier,
SegueToWebView, and I'm going to press Command+S to save.
| | 10:50 | We'll go back into our iPhone storyboard,
and we'll take a look at this segue, and we
| | 10:57 | see that's SequeToWebView. So, that's right.
| | 11:02 | It creates a WebViewController, a pointer to
that object. From our destinationViewController,
| | 11:11 | it grabs the IndexPath for the selected row,
and it uses that to find their correct item
| | 11:17 | record from the database, and it passes
that item record into the feedItem property in
| | 11:21 | the WebViewController.
| | 11:22 | So, this should all be familiar at this point.
| | 11:24 | We've done this several
times before in this course.
| | 11:27 | Now, if we come back in here to our
WebViewController, we can see we don't have any of the view methods
| | 11:33 | set up yet, so we'll just get a blank WebView.
But what will happen is when we press that
| | 11:37 | Action button, we should get a little NSlog
here that shows our feedItem URL, and that
| | 11:42 | will tell us that
everything is working properly.
| | 11:45 | So, I'll make sure the iPhone Simulator is
selected and press Run, and it builds with no issues.
| | 11:52 | Now, it will launch the iPhone Simulator.
| | 11:56 | And there it is. And if I bring up one of
these items from the blog for example, there
| | 12:02 | is our WebView, and there is all our buttons.
| | 12:05 | They look nice and they're perfectly spaced.
And if I clicked on this one on the right,
| | 12:09 | we should get a log item down here, and we
do. And there is that function signature,
| | 12:16 | and there's the URL.
| | 12:17 | So, these are the steps you will follow to
create most any view over the storyboard.
| | 12:22 | Now that we have the WebView working and we're
calling it successfully from the Items View,
| | 12:27 | now it's time to code the rest of the class,
and we'll start with that in the next movie.
| | 12:32 |
| | Collapse this transcript |
| Coding the web view| 00:01 | At this point, we have a working WebView,
and it's being called from the Item View when
| | 00:05 | the user selects an item.
| | 00:07 | Now, let's fill in the details in the class.
| | 00:09 | Let's make a working copy of our webView-01,
and you can use your results from the last
| | 00:17 | exercise, or I'm going to use the done version.
| | 00:21 | You can do that too as well. I'll rename this
webView-02 and open it in Xcode by double-clicking
| | 00:28 | on the Xcode project file.
| | 00:30 | And we're going to back out here to Finder
and grab these methods in this methods.txt file.
| | 00:39 | I'm just going to copy everything from right
up there all the way down to the end of that
| | 00:44 | section and come into Xcode and just place
that down here before the end. And we can
| | 00:53 | take a look at these.
| | 00:56 | You'll notice the viewDidLoad has a little red
exclamation point. That's because it's a duplicate.
| | 01:02 | There was a stub already in here, so I'm
just going to delete that stub and actually get
| | 01:08 | rid of our little red bang there.
| | 01:11 | So, viewDidLoad is called the after the view
is loaded and so it sets the title with the
| | 01:17 | title from the feedItem, and it does some
scaling and then it loads the request in the
| | 01:23 | webView, and you notice it's using the webView
property which in the .h file is actually an IBoutlet.
| | 01:30 | So, that's already been
hooked up in our storyboard.
| | 01:33 | So, that will load the request, and it's
going to want to call back the delegate methods,
| | 01:38 | and we'll load those up here in a minute.
| | 01:40 | ViewDidUnload, we just
clear out our properties.
| | 01:44 | When view is loaded again later, that just
prevents the same web page from showing up
| | 01:47 | again, which probably
won't happen anymore anyway.
| | 01:50 | Some earlier versions of
iOS, this was necessary.
| | 01:53 | viewWillAppear, it sets the
delegate for the webView to ourselves.
| | 01:58 | And viewWillDisappear, it stops loading if
the webView is still loading content, and
| | 02:04 | it clears out the delegate.
| | 02:05 | It also sets the
networkActivityIndicator to off.
| | 02:09 | Now, let's come back out here to
TextWrangler and load up the WebViewDelegate methods.
| | 02:21 | When the webView starts loading, this gets called
back, and we set our networkActivityIndicator to on,
| | 02:28 | we also set the status of the our back
button and forward button, whether they are enabled
| | 02:33 | or disabled, to a property from the webView,
which tells us whether or not it's possible
| | 02:39 | to go backwards or forward.
| | 02:40 | So, this makes our buttons work right, and
I'll show you that one when we load this up.
| | 02:45 | When it's done loading, we set the
networkActivityIndicator to no, which turns it off, and we set the
| | 02:54 | title in our title bar to this document title
which we grabbed from JavaScript, from the webView.
| | 03:02 | So, whatever the document title is set to in
the document itself, that will become the title.
| | 03:09 | So initially, we loaded the title up here
from our feedItem title, and now after the
| | 03:16 | page is loaded, we update the title
to whatever the document title is.
| | 03:20 | And in the event there is an error, we have
a little error document here which is just
| | 03:26 | literal strings, which says that we had an
error fetching the web page, and it fills
| | 03:32 | it in with the localized
description from the error object.
| | 03:34 | So, that's all really straightforward, and
I'm going to save all of this, and we're going
| | 03:39 | to go ahead and run it in the simulator.
| | 03:40 | Make sure the iPhone Simulator is
selected and press the Run button.
| | 03:45 | Now, we'll just go ahead and load
something from the lynda blog here.
| | 03:51 | And you see, there is our
networkActivityIndicator, and here is the document.
| | 03:57 | You see, we have our reload button.
| | 03:58 | If I press that, it reloads the page.
| | 04:00 | There's our network activity
spinner, and we have a stop button.
| | 04:03 | So, if I start the reload and
press stop right away, it stops it.
| | 04:08 | We have our action button,
which still isn't hooked up.
| | 04:12 | That will just display a log entry.
| | 04:13 | And you notice these back and
forward buttons. Those are grayed out.
| | 04:17 | I can't actually press them.
| | 04:20 | They're inactive but if I were to click on a link
inside of here--there's our networkActivityIndicator--
| | 04:26 | we've brought up a new page.
| | 04:28 | So, sometimes the back button doesn't light
up, even though it's supposed to, and if you
| | 04:31 | press the reload button, it makes it work.
And this is just a bug in the webView.
| | 04:36 | So now I can press this back button, and
you see there's our networkActivityIndicator,
| | 04:40 | and it's going back to the previous page.
| | 04:42 | And now, the forward button is lit up because that
is now active, and it's now possible for that to work.
| | 04:49 | So, if I press that, we'll go forward.
| | 04:50 | I press that, we go back.
| | 04:52 | So now, we have a working webview.
| | 04:55 | Again, using the Cocoa Touch
classes has made this very easy.
| | 04:58 | Most RSS feed items point to a web page, so
this solution makes it easier for us to display
| | 05:03 | that rich data without a lot of coding.
| | 05:05 | While our webView does display the content
nicely and has a few useful features for navigation,
| | 05:11 | it's not a full web browser.
| | 05:12 | So, we have a button for passing
control of the content to Safari.
| | 05:17 | When I press this button, of course
right now all we got is this log entry.
| | 05:21 | So, our remaining task is
to implement that button.
| | 05:26 |
| | Collapse this transcript |
| Viewing pages in Safari| 00:00 | Now, we have a working webView and we'd
like to be able to pass pages off to Safari as
| | 00:05 | a full-featured mobile web browser.
| | 00:07 | Let's see how we do that.
| | 00:09 | We'll navigate here to Chapter 6 of our exercise files,
and we're going to make a working copy of our webView-02.
| | 00:16 | I'm using the -done version.
| | 00:18 | You can use the version that you created if
you've been following along with the exercises.
| | 00:22 | I'm going to rename that to -03 and open it
up in Xcode by double-clicking on the Xcode
| | 00:30 | project file here in the project folder.
| | 00:33 | So, I'm going to minimize that and come back
up here to our actionButton method. And this
| | 00:42 | our actionButton that gets called whenever we
press that button. It has an NSLog right now.
| | 00:47 | So, if I run this in the iPhone Simulator,
and I'll bring up a page here, and you'll
| | 00:58 | notice when I press this button, instead of
launching Safari, it just displays this log
| | 01:02 | message that says the URL that's been called.
| | 01:07 | And so I'll come back here in
Xcode. I'm going to press Command+. to stop the simulator.
| | 01:12 | I'm going to minimize that down there,
and we're just going to replace this line with
| | 01:17 | the line of code that will actually call Safari.
| | 01:20 | And that looks like this.
| | 01:22 | It's a method call on an object.
| | 01:24 | So, I'm going to type in
UIApplication.sharedApplication and openURL.
| | 01:37 | And for the URL itself, I'm going
to call self.webView.request.URL.
| | 01:47 | And so the webView itself keeps a copy of the
URL for the current request that is being displayed.
| | 01:54 | And so that gives us the URL.
| | 01:56 | This openURL method is called on
UIApplication.sharedApplication.
| | 02:00 | So, UIApplication is a centralized
point of control for all iOS applications.
| | 02:07 | It provides access to system resources.
sharedApplication is a property that gives the application instance,
| | 02:14 | the individual application that you are using.
| | 02:17 | So, you call methods on the
sharedApplication property within UIApplication.
| | 02:22 | openURL is the system entry point for opening
a URL resource with the associated application.
| | 02:29 | Our HTTP or HTTPS
applications will open in Safari.
| | 02:33 | So, let's go ahead and run this in the
Simulator, pressing Command+S to save and clicking on
| | 02:40 | the run button here.
| | 02:42 | And now, when I bring this web page up
and I press this button here, it will load up
| | 02:50 | Safari with that URL.
| | 02:53 | So now, we have a complete working webView.
| | 02:57 | It's really a very simple task.
| | 02:59 | Cocoa Touch provides a simple delegate-based
webView that's powerful, flexible, and easy
| | 03:04 | to implement, and it works very well.
| | 03:09 |
| | Collapse this transcript |
|
|
7. Creating a Preferences PaneUnderstanding the iOS preferences system| 00:00 | The Settings app in iOS is the interface for
application preferences, like the Preferences app in OS X.
| | 00:07 | When you scroll down in its table view, you'll see a list
of installed apps that have preference settings available.
| | 00:14 | When you tap on one, you'll get the
settings pane for that application.
| | 00:19 | The settings for the application
are coded with a normal plist.
| | 00:22 | Let's take a look and see what that looks like.
| | 00:26 | This file here is a Settings bundle, and
if I right-click or Control Click on this, I
| | 00:31 | can say Show Package Contents, and
you'll see that here's a Root.plist.
| | 00:36 | That file name cannot be changed--well,
it can be, but it won't work anymore.
| | 00:40 | We've opened this file in a text editor here.
You can see this is TextWrangler.
| | 00:46 | You'll notice that this looks very much like a
normal XML file, and that's exactly what it is.
| | 00:53 | So a plist is a simple XML file, but
it's not very easy to edit this way.
| | 00:58 | This is the same file.
| | 00:59 | You can see it highlighted
down here being edited in Xcode.
| | 01:04 | This Property List Editor used to be a stand-alone
application, and now it's integrated into Xcode.
| | 01:09 | It provides a Simple Editing
Environment for the settings plist.
| | 01:13 | The plist is easily created and edited in
Xcode, and in the remainder of this chapter
| | 01:18 | we will create a settings bundle for
application and learn how to use it.
| | Collapse this transcript |
| Creating the preferences plist in Xcode| 00:00 | Oftentimes an application needs a simple way of
allowing users to choose some default values for settings.
| | 00:07 | You can do this with a modal dialog if you
have enough of a settings requirement to justify
| | 00:11 | the coding in UI investment.
| | 00:13 | The simple solution, however, is
to use the iOS settings interface.
| | 00:17 | Here, we have our BW RSS application
so far, running on the iOS Simulator.
| | 00:24 | And because again, the small screen that I'm
working on the iOS simulator has no border,
| | 00:30 | and it has no Home button.
| | 00:31 | So, if I want to press the Home button,
I press Shift+Command+H on my keyboard.
| | 00:36 | So if I want to change the settings, I need
to press the Home button and run the Settings
| | 00:42 | application and scroll down here till we see
our BW RSS application, and there's the Settings.
| | 00:51 | So, I can set the maximum items per
feed, and I can see the version number.
| | 00:56 | This is a very simple usage of the settings.
| | 00:59 | So, if you have just a few things to set,
or if you want to display a version number
| | 01:03 | or something like that,
Settings is a common way to do this.
| | 01:07 | So, I'm going to quit the simulator for now, and we're
going to come in here to our BWRSS-preferences-start,
| | 01:14 | and I'm going to make a working copy, and I'm
just going to rename this to BWRSS preferences.
| | 01:21 | We're not going to have a
separate version for a later movie.
| | 01:25 | And I'm going to launch it in Xcode by
double-clicking on the Xcode project file.
| | 01:31 | So, I'm going to go ahead and open up this disclosure
triangle, and I'm going to set the iPhone Simulator.
| | 01:39 | Under Supporting Files here, we don't yet have
it, but we're going to create a settings bundle.
| | 01:45 | So, I'm going to right click on Supporting
Files, and I'm going to say New File and under
| | 01:53 | iOS, Resource, I'm going to
select Settings Bundle and press Next.
| | 02:00 | Now, you want to leave this
default name because that's important.
| | 02:05 | It won't work if it's named something else,
and you wanted in the Supporting Files Group
| | 02:09 | and in your BWRSS targets.
| | 02:11 | So I'll press Create, and now
we have our settings bundle.
| | 02:15 | So, I'm going to go ahead and open that
disclosure triangle there, and I'm going to select the
| | 02:19 | Root.plist, and that's where we
actually are able to set the preferences.
| | 02:26 | You'll notice that there's a disclosure triangle
here next to this thing called Preference Items,
| | 02:31 | and there is four items in there and those are
just examples, and we're not actually going to use those.
| | 02:38 | You see, this interface is really weird.
| | 02:41 | When you click on stuff, stuff happens,
and you sometimes have to press Escape.
| | 02:44 | So, I clicked on that and then I pressed Escape.
| | 02:46 | I'm going to press my Delete Key here on
the keyboard and just delete those three items
| | 02:51 | and then I'm going to come back up here to
Item 0 (Group - Group), and I'm going to open
| | 02:55 | it up and under Title, I'm
going to call this, Feed Settings.
| | 03:00 | So, I'll select this over here, and I'll type
Feed Settings, and we're going to leave that
| | 03:08 | one as group as Type for this Item 0.
That fills out Item 0.
| | 03:13 | Item 0 has a type of group, and
it has a title of Feed Settings.
| | 03:18 | And then with this closed, I'm going to press
the Enter key, and it will create a new item.
| | 03:23 | I'm going to press Escape, and I'm going to
press Enter again, I'm going to do that several
| | 03:28 | times until I get four items here.
| | 03:30 | So, I have three new items under Item 0
Group, and we're going to fill these in.
| | 03:35 | The first one, Item 1, its
type is going to be Multi Value.
| | 03:39 | So, under Type, you'll notice
that the arrow is way over here.
| | 03:43 | I can select that and say Multi Value and
its title, I'll double-click in that space
| | 03:49 | there for the title is Max
Items Per Feed, like that.
| | 03:57 | It also gets an Identifier, and that's going to be
max_items_per_feed, and you notice it's an identifier.
| | 04:05 | It's going to be referenced from the code, so I
don't want spaces in, and I'm using underscores.
| | 04:10 | It's going to get a few more things, so I'm
clicking over here on the left because that
| | 04:14 | seems to be a fairly safe place to click, and I'm
going to press the Enter Key, and I get a new item.
| | 04:20 | This item is going to be a default value so
that's correct, and its type is going to be a number.
| | 04:27 | And over here, I'm going to double-click on
that number it'll allow me to edit it, and
| | 04:30 | that's going to be 50.
So, it's going to have a default value of 50.
| | 04:34 | And when I press Enter, this editor is
just really weird for these types of items,
| | 04:39 | so you have to bear with it a little bit.
So, that's default value.
| | 04:43 | And then we're going to have a new item called
Titles, and I'm just going to close that real
| | 04:47 | quick and make another one
and a new item called, Values.
| | 04:50 | So, it actually defaults to the correct thing
here, and if it doesn't, you can always press
| | 04:54 | that little arrow to the right of the field that you
want to change, and you get an opportunity to change it.
| | 04:59 | So under Titles,
there's going to be four items.
| | 05:02 | Again, cursor jumps around, two, three, and four.
Under values, there's going to be four items.
| | 05:14 | So, I open that up, and I
have one, two, three, and four.
| | 05:20 | All of these values are going to be type Number.
| | 05:23 | So we'll just go ahead
and change those right now.
| | 05:29 | And their values are going
to be 25, 50, 100, and 250.
| | 05:37 | And then the titles for each of these items
are going to be Strings and they're just going
| | 05:45 | to say the same thing, 25.
I guess I could spell it out if I wanted to.
| | 05:49 | So, these will be the labels that will
be next to each of these values, 250.
| | 05:53 | We'll see how these looks like in
the Settings app in a little bit here.
| | 05:58 | So, there is our titles and our
values, and that's Item 1 Multi Value.
| | 06:03 | Item 2 is really just a spacer.
It creates an empty space.
| | 06:09 | So, its Type is a Group and its Title is blank, and
there is no key, so I'm just going to delete that one.
| | 06:20 | Item 3, its Type is Title, and its Title
is Version, its Identifier is BWRSS_Version
| | 06:31 | like that with an underscore, and it's
going to have a Default Value, and we'll just
| | 06:38 | give it a version value here,
3.1.0, and there, we're done.
| | 06:43 | So, I'm going to press Command+S to save this.
| | 06:47 | Now again, to press the Home button, I'm pressing
Shift+Command+H, and I'm going to come out here to the Settings.
| | 06:56 | I'm going to scroll down here, and there is
our BWRSS, and there we have our Max Items
| | 07:01 | Per Feed, which we can change, and we have our
Version, which when we click on this, nothing happens.
| | 07:07 | And you see our title there,
it says Feed Settings.
| | 07:09 | So, that's all of these things here.
| | 07:10 | So now that we have a working settings bundle,
we need to be able to read these values in our code.
| | 07:16 | And we'll take a look at how
to do that in the next lesson.
| | Collapse this transcript |
| Reading preferences in your application| 00:00 | Now, we have a working settings bundle.
| | 00:02 | We need to be able to
read the values in our code.
| | 00:05 | In this movie we'll look at how that is done.
| | 00:07 | We'll go ahead and open the BWRSS-preferences
that I created in the last movie.
| | 00:12 | I'm going to open that in Xcode by
double-clicking on the .xcode project file.
| | 00:18 | So, this is the version that has
these preferences Root.plist in it.
| | 00:24 | I'm going to come up here
into RSSDB and open the RSSDB.m.
| | 00:31 | So, this is our data base routines, and I
just closed the Utilities panel because we
| | 00:38 | don't need that, it's taking up space here.
| | 00:40 | If we look down here at the deleteOldItems, we'll
see rather along SQL Query it takes up two strings.
| | 00:48 | Two strings right next to each other in C or C++ or in
Objective-C concatenates these two strings together.
| | 00:57 | Objective-C has borrowed this
even with its NSString literals.
| | 01:00 | So, this is really all one string delete from blah,
blah, blah, all the way up to this closing quote here.
| | 01:07 | Now, what we have here is a subquery.
Everything in the parenthesis is a subquery.
| | 01:12 | So, it's selecting items from the items
table and ordering them by publication date with
| | 01:19 | a limit of getMaxItemsPerFeed and then it's
deleting everything that is not in that list.
| | 01:26 | It's taking the newest so many items,
and it's deleting everything else.
| | 01:31 | So, this getMaxItemsPerFeed is the number of
items that's being retained when we delete old items.
| | 01:38 | So, where is that defined?
| | 01:40 | So, getMaxItemsPerFeed is up here, and this
is actually reading from the user default.
| | 01:49 | NSUserDefaults grabs our UserDefaults, and we
can get our MaxItemsPerFeed by using objectForKey
| | 01:57 | and using this string max_items_per_feed,
and if we remember, down here in our Settings
| | 02:02 | Bundle, our identifier MaxItemsPerFeed is
used for that Multi Value where we select
| | 02:11 | either 25, 50, 100 or 250.
| | 02:15 | You'll also notice that if we don't have a
MaxItemsPerFeed, we set it to a default value,
| | 02:20 | and that kDefault, that's defined in
our header file and the default is 50.
| | 02:26 | So let's run a little experiment here.
| | 02:28 | I'm going to grab the MaxItemsPerFeed value,
and I'm going to display it with NSlog.
| | 02:34 | And I'm also going to display it
after we've tested it for default.
| | 02:43 | Press Command+S to save.
| | 02:44 | I'm going to go ahead and
run this in the simulator.
| | 02:47 | It's an interesting test because it's going to show
one of the limitations of this settings interface.
| | 02:53 | Now, the first thing I'm going to do when
I run this in the simulator, I'm going to
| | 02:56 | come over here to our iOS Simulator menu, and
I'm going to say Reset Contents and Setting,
| | 03:01 | and I'm going to press this Reset button.
| | 03:04 | That resets my simulator to its default
as if it's a brand-new phone out of the box,
| | 03:08 | and you'll notice that it's also finish
running and so I have to run it again.
| | 03:13 | Now, we're running this fresh
having never gone to the settings before.
| | 03:19 | When I select one of these feeds, this MaxItemsPerFeed
gets called, and you'll notice that the value
| | 03:28 | from the settings is null and
after the test for default is 50.
| | 03:32 | So, we get a null the first time we call it.
| | 03:35 | Until you've actually gone to the settings
app and selected something, you're not going
| | 03:39 | to get a value and so you have to have a
default value and set it to that default value, in
| | 03:45 | case what you get from your
settings is actually null.
| | 03:48 | Now, when I come out here to my settings--
I'm going to press the Home button here.
| | 03:51 | I'm pressing Shift+Command+H on my keyboard
for the Home button because my screen doesn't
| | 03:56 | have one because this is getting scaled down.
| | 03:59 | I'm going to slide over here and grab the
settings app and come over here to BW RSS.
| | 04:05 | If I select 50 again, and I double tap my
Home button and come back to my BW RSS app,
| | 04:11 | and I'll select this again, you'll see that
I'm still getting that null, but if I come
| | 04:18 | out here to my settings app,
and I select a different value.
| | 04:21 | I'll select 100 and press, double tap my Home
button and come out here and select my Feed again.
| | 04:29 | Now, you'll notice that I'm getting that
100 value from the settings and after the test
| | 04:34 | because it's not null, I go
ahead and I leave that value alone.
| | 04:38 | So, when you run your app, you're not
going to actually get values from your settings
| | 04:43 | until somebody goes to the settings
app and actually changes something.
| | 04:47 | Even just looking at the value or selecting the
default value again, doesn't change something.
| | 04:52 | Now, when I bring it back to 50
again, now I'll pick up that 50.
| | 04:58 | So, there we have the 50
before and after the test.
| | 05:02 | So, we'll go ahead and
delete my NSLog items here.
| | 05:07 | Now, we can see that using the iOS Setting
Interface is really quite easy, you just create
| | 05:13 | the settings bundle, you put it in your settings,
and you use the NSUserDefaults class to read those values.
| | Collapse this transcript |
|
|
8. Adding a FeatureAdding pull-to-refresh functionality| 00:00 | In this chapter, we're going to talk about the
process of adding a new feature to our working code.
| | 00:05 | After writing this application and submitting it to
the App Store, I decided that I wanted to add a feature.
| | 00:10 | I noticed that when I use the app, I would
sometimes leave it for a while with the item
| | 00:15 | view displayed, and after an hour so I would
return, and I'd want to refresh the list to
| | 00:20 | see what was new in that feed without having to
return to the review and select the feed again.
| | 00:25 | So, I decided to add a feature that
would refresh the item view on demand.
| | 00:29 | The first task when adding a feature is
to think about how the user will use it.
| | 00:33 | What will the interface look like?
| | 00:35 | One of the things I like about BW RSS is
the interface is simple and uncluttered.
| | 00:40 | I didn't want to add a button, especially on the
iPhone platform with its limited screen space.
| | 00:45 | Before iOS 6, there was no accepted gesture for
reloading a page, so I opted to use the shake gesture.
| | 00:51 | I'd seen it's done before, and I liked how it works,
a gentle shake of the phone and the feed refreshes.
| | 00:57 | Beginning with iOS 6, however, iOS now provides a pull
to refresh feature, which is perfect for this purpose.
| | 01:05 | So here it is already
implemented in the emulator.
| | 01:08 | When I select a feed, I can pull and I get
this little icon, and when I let go it refreshes.
| | 01:15 | Well, there's nothing there.
So I'm going to add a feed.
| | 01:19 | This is a test feed that I've created.
It's cleverly called Test Feed.
| | 01:24 | It's at ios.bw.org/testfeed,
and you're welcome to use this.
| | 01:30 | In this feed, every time it gets
called, it gives you five new items.
| | 01:35 | So when I refresh, I'll get five
new items and these are random items.
| | 01:40 | The source code for this feed is in
Libraries, and it's in testfeed.py.
| | 01:45 | It's a Python script, and this is
the implementation of this test feed.
| | 01:51 | It's not a very long script.
| | 01:52 | It's about 150 lines long,
and you're welcome to use it.
| | 01:56 | Certainly, if you're going to be using this
a lot, I'd rather that you would run it on
| | 01:59 | your own server than on mine, but if
you're just going to use it occasionally,
| | 02:03 | you're welcome to run it on my server.
| | 02:05 | And there's a lines.txt file here, which is
used, has a bunch of little random disclaimers
| | 02:10 | in it that I collected at one time in my life.
Those are the headlines of these feeds.
| | 02:17 | Batteries not included, Penalty for
private use, Some assembly required, et cetera.
| | 02:23 | So that's the pull gesture.
When you pull on it, it refreshes.
| | 02:28 | And in the next movie, I'll show you how to implement
the pull to refresh feature for iOS 6.
| | Collapse this transcript |
| Implementing the pull-to-refresh gesture for iOS 6| 00:00 | Now let's take a look at how to
implement the iOS 6 pull to refresh feature.
| | 00:04 | I'm going to create a working copy of our
BWRSS-features-start, and I'm going to rename
| | 00:14 | that as -02, and we'll open in Xcode by
double clicking on the Xcode project file.
| | 00:24 | We're going to be working in the ItemsTableViewController,
and we notice up here at the top in our interface
| | 00:31 | section, this is the private members or
the instance variables for this class.
| | 00:36 | You'll notice there's a BOOL variable called
canRefresh, and that gets set in -viewDidLoad.
| | 00:41 | If I come down to -viewDidLoad, and we're
going to come out to our Finder and load up this
| | 00:49 | methods.txt file, and
here is our new -viewDidLoad.
| | 00:53 | I'm going to just copy that and come over
here in to Xcode and I will paste that in place.
| | 01:00 | So, you'll notice here I used the respondsToSelector
method to check if our TableViewController
| | 01:08 | response to the selector called refreshControl,
and if it does, then I know that we can use
| | 01:14 | Refresh so I can set this canRefresh
equals true, or Yes in the case of Objective-C.
| | 01:19 | And I initialize our refreshControl with
the UIRefreshControl and then I call addTarget
| | 01:25 | on it to add the target, it is refreshInvoked:forState
method, and it will be set for the control
| | 01:34 | events, UIControlEventValueChanged.
| | 01:37 | It looks a little bit complicated, and I
guess it is, but this is just how you do it, and
| | 01:42 | you'll pretty much
always do it exactly this way.
| | 01:44 | So, we're going to come back out here to
our text editor, and I'm going to grab all of
| | 01:48 | this support for reload gestures stuff here,
and I'm going to come back into Xcode, and
| | 01:55 | I'm going to paste it in right about here.
Here is how this works.
| | 02:02 | Here's our refreshInvoked forState, and
you'll remember that's the selector that we set as
| | 02:08 | our target for the refreshControl event, right?
| | 02:12 | So, when that gets called we reload our RSSFeed, and we
send endRefreshing message to the RefreshControl, as simple as that.
| | 02:22 | In order for that to happen, we have to be
the first responder and so our class gets
| | 02:27 | called with canBecomeFirstResponder to test
whether or not we have that capability and
| | 02:32 | so we use this canRefresh flag to return there.
| | 02:35 | When viewDidAppear, we're going to call becomeFirstResponder,
and when view will disappear we resignFirstResponder.
| | 02:44 | So that's all there is to it.
| | 02:46 | When I save this and run it in the iPhone
Simulator--I'm going to go ahead and add my
| | 02:52 | test feed here so that's at ios.bw.org/testfeed,
and I'll bring that up.
| | 03:01 | There is five entries, and when I pull you
see there is that little icon, and I pull
| | 03:07 | it far enough, and it goes ahead it calls
this rerefreshInvoked which reloads the feed and
| | 03:14 | ends the refreshing, and there it is again.
| | 03:18 | So, we've now successfully added the
pull to refresh feature to our app.
| | 03:23 | This was easy for a number of reasons.
| | 03:25 | First of all, Cocoa Touch Framework is
really very well written and easy to code to then
| | 03:30 | reloading the RSS Feed was also easy
because our code is well-organized.
| | 03:34 | If you do a good job of keeping your code
clear and concise, then changing and modifying
| | 03:38 | your code will always be a lot easier.
| | Collapse this transcript |
|
|
9. Creating a Universal ApplicationUnderstanding split view| 00:00 | When the iPad was first introduced, a number
of people made comparisons to a giant iPhone
| | 00:05 | that couldn't make telephone calls.
| | 00:07 | The reality turned out to be
something quite a bit more brilliant.
| | 00:11 | And as a result, well, the iPad has become very popular,
selling many millions of units in these first few years.
| | 00:17 | The beauty of the iPad interface
is in its large screen area.
| | 00:22 | The SplitViewController is
an excellent example of this.
| | 00:25 | It combines two views, the master view, often a
table view and the display view, in this case a webView.
| | 00:32 | In landscape mode the master view is
usually on the left side occupying roughly a third
| | 00:37 | of the screen, and the display view is on
the right using the remainder of the screen.
| | 00:42 | In Portrait mode the display view may command the entire
screen and the master view is accessed with a pop-up controller.
| | 00:49 | This is all accomplished with a programming
interface that's consistent with the rest
| | 00:53 | of the Cocoa Framework, making it very easy
to implement, especially if you have experience
| | 00:58 | writing iPhone or Cocoa Applications or
even another event driven architecture.
| | Collapse this transcript |
| Coding the table views| 00:00 | At this point we have a complete iOS
Application that's targeted for the iPhone form factor.
| | 00:05 | In order to take advantage of the iPad, we
will implement a separate storyboard and a
| | 00:10 | few additional classes for the iPad platform.
| | 00:13 | This application was started as a universal app
with the intention of supporting the iPad platform.
| | 00:19 | We can use most of our
existing code on the iPad.
| | 00:22 | We will need some new code to support the
split view and a few other features, but mostly
| | 00:26 | we will just be sub-classing our
existing classes and overriding a few methods.
| | 00:31 | Let's start by making a working
copy of BWRSS split view start.
| | 00:36 | I'm going to rename this to splitview-02, and
I'm going to open it in Xcode by double-clicking
| | 00:43 | on the Xcode project file.
| | 00:46 | Now let's take a quick look at the iPad
storyboard, and you notice that I really need to zoom
| | 00:54 | this out on this small screen
to be able to really see it.
| | 00:59 | Here we have the SplitViewController--and I'll
zoom in a little bit more here so you could see that.
| | 01:07 | So there we have the SplitViewController with
the MasterViewController on the left and the
| | 01:11 | DetailViewController on the right, and it
connects to two navigation controllers, one
| | 01:17 | for the master and then another one
down here for the DetailViewController.
| | 01:22 | Then we have the view controller themselves as the
DetailViewController, and for the MasterViewController
| | 01:29 | it's been renamed, and it was renamed in one
of our movies to be a FeedsTableViewController,
| | 01:36 | but it's still not set up in the story.
| | 01:38 | Down here in the DetailViewController,
this is where we are going to put our webView,
| | 01:43 | and for the left side, we'll
mostly leave the left side alone.
| | 01:47 | Most of what we need to do is to update the
WebViewController to work as a detailed view.
| | 01:51 | So let's go ahead and create a class.
I am going to come up here to File > New File.
| | 01:57 | Under iOS > Cocoa Touch > Objective-C class,
and I'm going to press Next, and this is going
| | 02:02 | to be BWRSSDetailViewController, and it's
going to be subclass of BWRSSWebViewController.
| | 02:14 | It's right there, and we're going to ahead
and leave these alone for now, because we're
| | 02:17 | going to filling that in from some results.
| | 02:20 | It gets stored in here and its group is right
and its target is right, and that's all correct.
| | 02:26 | So I'm just going to press
Create, and there are our files.
| | 02:32 | Now I'm going to come out here to our Finder,
and I'm going to open BWRSS-detailview-02-header.
| | 02:40 | I'm just going to grab these two lines here
and copy them, and I'll just close that and
| | 02:45 | come back out here to Xcode and come into
the header file, and I'm just going to replace
| | 02:50 | this one line with those two.
| | 02:52 | So basically what we have here is that DetailViewController
subclass of the WebViewController, and it
| | 02:59 | implements these protocols of UIPopupControllerDelegate
and UISplitViewControllerDelegate.
| | 03:06 | And there's one property detail item.
| | 03:08 | Actually, it's going to get filled in from
the item's table view controller, but that'll
| | 03:12 | happen later on in this chapter.
| | 03:13 | We're going to come back out here to Finder,
and I'm going to open this detailview methods.txt,
| | 03:20 | and there's just a little bit of code
here that we want to put in the .m files.
| | 03:24 | So I'm going to Command+C, copy that, and
close my text editor, come back out here to Xcode,
| | 03:30 | and go into the methods file, and I'm just
going to replace these two lines with all of that.
| | 03:38 | So here we have a couple instance variables,
and we have this property for the Toolbar
| | 03:43 | and a property for webTitle.
In fact, these are IBOutlets.
| | 03:47 | This webTitle one, it's not actually used in
this implementation, but it's there because
| | 03:52 | it's referenced in some of the code.
| | 03:55 | That's all we need to do
for right now in these files.
| | 03:58 | We'll get back to them later on this chapter.
| | 04:00 | I want to come back out here now to the iPad
storyboard and get this set up for the iPad
| | 04:06 | side of our universal application.
| | 04:09 | We're going to start with
our navigation controller.
| | 04:11 | I'm just going to select that.
| | 04:12 | I'm going to bring up the properties over
here and in the Attributes Inspector under
| | 04:18 | Simulated Matrix where it says Top Bar we
are going to say None, and that will get rid
| | 04:24 | of this top navigation bar, because we're
going to create our own, and that way we can
| | 04:28 | put buttons on it, and we can make it
also our navigation bar for the web view.
| | 04:33 | We're going to come out here to our Objects,
and we're going to grab a toolbar, and we're
| | 04:39 | going to drag it up here to the top of--it looks
like I need to zoom in in order for that to work.
| | 04:44 | I'm going to drop that up here at the top.
| | 04:51 | This is our DetailViewController, and
that's this one at the end of the chain there on
| | 04:58 | the bottom, and I'm also
going to grab a web view.
| | 05:06 | I'm going to drop that in right here, and I'm going to
have it fill up the rest of this space, and there we go.
| | 05:18 | So now we have web view,
and we have the tool bar.
| | 05:20 | Now we can go ahead and add
some buttons to our tool bar.
| | 05:25 | There's already one button up there.
| | 05:26 | There's going to be five buttons, and this
time there's going to be spaces on either
| | 05:31 | side of the buttons as well.
| | 05:32 | So we're actually going to
have six of these flexible spaces.
| | 05:34 | I'm going to start by putting this flexible
space there and another one there and then
| | 05:42 | three, four, five, and six.
| | 05:48 | Then I'm going to put in some more buttons
two, three, four, five, and you'll see in
| | 05:58 | a little while why it is that we do it this way.
| | 06:00 | But the bottom line is that another button
will be dropped in here programatically so
| | 06:06 | that when it's in portrait we can still bring
in that table view on the left side as a pop-over,
| | 06:12 | and that button will automatically be added and
removed, and this actually makes all of that look right.
| | 06:18 | Now we'll go ahead and we'll style buttons
so each of them is going to be playing and
| | 06:22 | these are the buttons for web view so
already know what goes in them, because we did that
| | 06:26 | before earlier in the course.
| | 06:28 | This first one here is going to be the Back Arrow,
and this next one is going to be the Forward Arrow.
| | 06:41 | Then this one here is going to be the
Refresh button and then this one will be the Stop
| | 06:50 | button, and this one here
will be the Action button.
| | 06:54 | So I have to click this a few times in
order for it to select that Action button.
| | 07:00 | Now we see that our toolbar
looks exactly how we want it to look.
| | 07:03 | So we come down here to the controller itself
and over to the Identity Inspector, and that's
| | 07:11 | correct BWRSSDetailViewControllers.
So that's exactly what we want there.
| | 07:16 | Then we're going to come over here to the
Connections Inspector, and you see one here
| | 07:21 | that says detail description, and it's got
an exclamation point next to that, because
| | 07:27 | there isn't one in this
DetailViewController, so that's okay.
| | 07:30 | We're just going to delete that, and you see
we have this web view outlet, and it connect
| | 07:35 | that to our web view, and we have a
Toolbar outlet, so we're going to connect that to our toolbar.
| | 07:41 | While we are at here we've got Back button.
| | 07:44 | We can select our Back button item and our
Forward button, select our Forward button item.
| | 07:50 | If we scroll all the way down here,
we'll see our Action button item.
| | 07:54 | We can connect to that one up.
| | 07:56 | Then we select the web view which I'm just
going to tap on it here, and that will give
| | 08:00 | us a whole new selection of buttons, and this
connects them to the web view so that if they're
| | 08:04 | connected up to the web view, we
don't have to do them programatically.
| | 08:08 | So that's the Go Back button and the Go Forward
button and the Reload button, and the Stop Loading button.
| | 08:18 | Those are all now hooked up as well.
| | 08:20 | We can scroll up, and we can work on
our FeedsTableViewController a little.
| | 08:25 | So if I select that and come up here to
the top of it, and you see it says Master.
| | 08:31 | I'm just going to double-click there, and I'm
going to change that to--I'm going say BWRSS.
| | 08:36 | That will make it consistent, BWRSS.
| | 08:38 | And I'm going to select the Prototype Cell
and come over here to the Attributes inspector
| | 08:46 | and the Identifier is going to be
FeedsCell and press Enter there.
| | 08:56 | The style is Subtitle, and you'll notice
Accessories says None, but that's going to change when
| | 09:02 | I create a segue from it.
So we'll have to update it again.
| | 09:06 | Now we have this FeedsTableViewController
set up properly, and it's time for us to create
| | 09:12 | our ItemsTableViewController.
| | 09:14 | So we're going to grab another
TableViewController here, and I'm going drop it in right there,
| | 09:21 | and it's going to create this whole giant
thing, and it's just unfortunate the way that
| | 09:26 | it does this especially on a small screen.
| | 09:28 | So I'm just going to set it over to the
side here and what's going to happen is when I
| | 09:32 | create this segue, that will
resize the correct size. So watch this.
| | 09:37 | I'm going to Control click on this Prototype
Cell over there in the FeedsTableViewController,
| | 09:44 | and I'm going to drag it over here and let
go and select Push segue and notice that it
| | 09:50 | now resizes properly, because it's inheriting
that from the view that that's being connected
| | 09:57 | to it, from its parent view.
| | 10:00 | You will also notice that this Prototype Cell over
here got changed to have little disclosure indicator.
| | 10:05 | So I'm going to select that back
to none and press Command+S to save.
| | 10:11 | Now our new TableViewController, we are
here in the Identity Inspector, that's going to
| | 10:16 | become our ItemsTableViewController and our segue is
going to get an identifier to SegueToItemTableView.
| | 10:30 | And I'm going to come over here to our
Prototype Cell and make that a Subtitle and our Reuse
| | 10:36 | Identifier is going to be ItemCell.
ItemCell like that.
| | 10:43 | Now that we've got all this set up, I'm going
to press Command+S to save, and we'll go ahead
| | 10:48 | and we'll run it in the iPad Emulator.
So you can see we have that error.
| | 10:57 | I press Command+Left Arrow to rotate it,
and now we can see our Table Views are working.
| | 11:04 | So this is pretty much how we
expect it to work at this point.
| | 11:08 | We've done very, very little coding.
All we've really done is set up the storyboards.
| | 11:12 | So we have the Split View
hooked up in the storyboard.
| | 11:16 | This is the foundation for the rest of
our work for the iPad side of our app.
| | 11:20 | The next step is to plug in the
code so it shows pages in the web view.
| | Collapse this transcript |
| Implementing the iPad detail view| 00:00 | Now that we have the feed and item views
running, it's time to implement the Web view.
| | 00:05 | For the iPad version, we're using the
Detail View controller for the Web view.
| | 00:11 | Let's make a working copy of BWRSS-splitview-02.
I'm going to use the done version here.
| | 00:17 | You can use what you completed
in the last movie if you like.
| | 00:22 | I'm going to call this BWRSS-splitview-03, and
I'm going to open it in Xcode by double-clicking
| | 00:29 | on this Xcode project file.
| | 00:31 | Now, here in our BWRSSDetailViewController--I'm
going to expand this a little bit so we can see that--
| | 00:37 | see that we have our local instance
variables and properties here, and we have just three
| | 00:45 | little methods inside of this implementation.
I'm actually going to delete all of that.
| | 00:50 | And then I'm going to come back out here to the
Finder, and here, we have our detailview-03-methods.txt
| | 00:57 | file and grab that, and I'm just
going to select the whole thing.
| | 01:00 | You can see there's a lot of typing here, and
we'll go through it, and I'll explain in a little bit.
| | 01:04 | I'm going to select all of that and press
Command+C to copy and come in here to Xcode,
| | 01:10 | and right there with the cursor just before
the end marker, I'm going to press Command+V
| | 01:14 | to paste it all in there.
| | 01:15 | So, there are a few
interesting things going on here.
| | 01:19 | You'll notice in this section called set
up the view when called, there's a method
| | 01:25 | called setDetailItem, and this
is an Objective-C convention.
| | 01:29 | When you have a property called detailItem--
you see it's a property with the @property--
| | 01:34 | the compiler will create a
setDetailItem and getDetailItem methods.
| | 01:41 | And here, I'm overwriting that setDetailItem
method, so when another object assigns something
| | 01:48 | to that variable, to that property,
the setDetailItem gets called.
| | 01:53 | And by overloading it, it allows me to do some
other things during the process of setting that value.
| | 02:00 | So here I just set the value at the top, and
then I call configureView which is this method
| | 02:05 | above there, and I dismiss
the pop-over if there is one.
| | 02:09 | And so, what I know is that when that variable
is getting set, somebody has tapped on something
| | 02:14 | because it's in that Items View Controller,
and we'll set this up in a moment where that
| | 02:19 | property is getting set, and so
that's where this is being called.
| | 02:22 | So that allows me to intercept that action.
| | 02:25 | So it calls configureView which then sets up our
NSURLRequest and calls the webView with that URL.
| | 02:35 | So, the URL is getting passed in this
newDetailItem, and that's getting assigned to feedItem up
| | 02:41 | here, and feedItem is
being used to load the webView.
| | 02:46 | Then down here, we have some
what I call split view support.
| | 02:50 | When the left view is hidden, that
means that we're in Portrait mode.
| | 02:54 | We grab our button bar, and we add a button
to it on the left, and we use that to control
| | 02:59 | the popoverController.
| | 03:01 | When the left view is shown again, we undo that.
| | 03:05 | This is the setPopoverButton and
clearPopoverButton methods that are being called by those.
| | 03:11 | So, I'm going to press Command+S here, and
I'm going to come back out to TextWrangler,
| | 03:15 | and I'm going to close that, and come out to finder, and
I'm going to load up our ItemsTableViewController-03-methods,
| | 03:24 | and there's really just a
couple of things in here.
| | 03:26 | So we'll come back over to Xcode, and we'll
select our ItemsTableViewController.m file.
| | 03:32 | First thing I want to do is I want
to grab this import, copy and paste.
| | 03:37 | And that's for our DetailViewController because
we're going to be setting a property on that object.
| | 03:43 | We'll come back out here.
And here's our didSelectRowAtIndexPath.
| | 03:47 | This is what gets called when you touch a
row on a table, and that's selecting it.
| | 03:54 | And if I come up here, we can find that,
there it is didSelectRowAtIndexPath.
| | 03:59 | I'm just going to take this whole method,
and I'm going to select the whole thing and
| | 04:04 | press Command+V to paste in what we've copied
out of that text file, and Command+S to save.
| | 04:10 | So, this is really simple, if (haveSplitViewController), and
we set that up above in either viewDidLoad or viewWillAppear.
| | 04:22 | Here it is, viewWillAppear.
| | 04:24 | If we have a SplitViewController,
then we have a SplitViewController.
| | 04:28 | We're setting that flag.
| | 04:30 | And that flag is declared up
here as a BOOL in our private members.
| | 04:38 | So if we have a SplitViewController,
that means that we're on an iPad.
| | 04:42 | Now, I could have tested it for the iPad idiom.
| | 04:44 | But really, what I'm concerned with
here is do we have a SplitViewController?
| | 04:47 | Because if we're on an iPad, and we don't have a
SplitViewController for some reason, then this is moot.
| | 04:53 | So, if we have a SplitViewController, then we
go ahead and we get the DetailViewController,
| | 04:59 | and we get the WebViewController
from our viewControllers array.
| | 05:03 | And in our WebViewController, we assign to
this detailed item, and you remember, that's
| | 05:08 | going to call our setDetailItem
method that we looked at a few minutes ago.
| | 05:13 | We're going to pass it that itemRecord,
and that will make everything work.
| | 05:16 | So now I'm going to save this,
and we select our iPad Simulator.
| | 05:20 | We're going to go ahead and run it.
So now we're in Portrait mode.
| | 05:24 | I'm going to press Command+Left Arrow,
and that will put us in Landscape mode.
| | 05:27 | And now we have our master
control over here, our table views.
| | 05:32 | And over on the right, we
have our detailController.
| | 05:34 | And so, if I select one of these, and I select
one of these, you see our little spinner running
| | 05:39 | up there, and there's our web page and
our WebView and our DetailViewController.
| | 05:43 | Now, here's the interesting thing.
| | 05:45 | If I go ahead and just select a different
item here, you notice we get a different page,
| | 05:49 | and now this Back button is lit up.
| | 05:51 | So I can press that and
get back to the previous page.
| | 05:54 | Unlike the iPhone form factor, this webView is in a
separate pane here, and so it's visible all the time.
| | 06:01 | So there is no reason to clear it out anymore.
| | 06:05 | If I come over here, and I load up something
from the lynda blog, there it is, I can go
| | 06:10 | back to the previous page that I was at.
| | 06:13 | So these Back and Forward buttons, they're active
more often than they are in the iPhone form factor.
| | 06:20 | Now the webView is working and
so the app is mostly functional.
| | 06:23 | It's really a straightforward process
when you know the ins and outs of it.
| | 06:26 | Subclassing the existing
webView makes it especially easy.
| | 06:30 | The next step is to implement the
AddView modal dialog box, and then we'll be done.
| | Collapse this transcript |
| Implementing the iPad modal view| 00:00 | Now that we have a working groupView, itemView,
and webView, it's time to implement our addView.
| | 00:06 | The tactic is pretty simple.
| | 00:08 | We're going to create the controller in the storyboard,
connect it to the BWRSSAddViewController class,
| | 00:13 | and set up the form elements.
| | 00:17 | Let's start by making a working copy of BWRSS-
splitview-03, and I'm just using this done version, you
| | 00:24 | can use the version that you created in
the last lesson if you're following along.
| | 00:28 | I'm just going to rename it to BWRSS-splitview-04,
and I'm going to open it in Xcode by double-clicking
| | 00:35 | on this Xcode project file.
| | 00:38 | So now we're going to come up to the iPad
storyboard, which is right up there, and I'm
| | 00:45 | going to open up our
Utilities View and scroll over here.
| | 00:51 | You see we have the Items Table View
Controller right there, and right under it I'm going
| | 00:56 | to go ahead and put our Add View Controller.
| | 00:59 | So, the first thing I'm going to do is I'm going to add
that button up here on our Feeds Table View Controller.
| | 01:07 | So, come over here to Objects and Windows
& Bars and grab a Bar button Item and just
| | 01:15 | drop it right there and call it an Add button so it
gets that little Plus and Command+S to save.
| | 01:23 | Now, under our Items View Controller, I'm
just going to scroll on over here, and I'm
| | 01:27 | going to create a new controller.
| | 01:30 | This is just a plain View Controller, so I'm
going to drop it in right here and its size
| | 01:37 | is going to be Form Sheet so that makes it
manageable right there and drag it down over here.
| | 01:47 | The Transition Style will say Cover Vertical.
The Presentation will say Form Sheet.
| | 01:54 | And then we're going to set our controller to the
BWRSSAddViewController, and I'm pressing Command+S to save.
| | 02:02 | Now, I'll scroll down here so we can see a
little bit more of it, and under Data Views
| | 02:09 | I'm going to grab a Scroll View, and I'm going to
drop that right here on this controller like that.
| | 02:19 | Get it all nice and lined up, and over here,
in the Attributes Inspector, let's scroll
| | 02:26 | down and give it a background of
Scroll View Textured Background Color.
| | 02:34 | Now, the rest of this is going to be very
much like what we did when we first created
| | 02:38 | the addView controller for the iPhone.
We can go ahead and populate this view.
| | 02:44 | I'm going to start with a text label up here
at the top, and I'll stretch that out to sides
| | 02:52 | using these handy dandy guides
that the Interface Builder gives us.
| | 02:56 | I'm going to make it centered and
make it the System Bold Font 17 size.
| | 03:07 | It's going to say Add RSS Feed, like
that, and then I'm going to make it white.
| | 03:15 | This background is actually dark. It
doesn't look like it's dark, there it is.
| | 03:21 | But you'll see when we run it in
the Emulator that it actually is dark.
| | 03:25 | Now we can grab another text label and put
it right under this one, and we're going to
| | 03:29 | leave this one left flush, and this is our
Status Label, and it's going to say Enter a URL.
| | 03:42 | This one is going to be Size 12,
and it's also going to be white.
| | 03:47 | Now, we're going to grab a text field and
put that right under that other text label
| | 03:53 | and bring that over to the sides and the
text in there will say http://, and we're going
| | 04:02 | to scroll down here and set some properties
here, Capitalization: None, Correction: No,
| | 04:08 | Keyboard: URL, Appearance: Default,
and Return Key: Done.
| | 04:12 | So, now I'm going to grab a couple of
buttons here, Round Rectangle Buttons, and I'm going
| | 04:18 | to drag them over here and align them up.
| | 04:21 | This little technique for doing this, and
I'm going to grab them both and center them,
| | 04:27 | and then the button on the left will be called
Cancel, and the button on the right will be called Add.
| | 04:35 | Now, we're going to connect our segue, and
that will be a little bit challenging with
| | 04:40 | this being zoomed in like this.
| | 04:43 | Let's see if we can do this with it zoomed out.
I'm not sure that it will let me.
| | 04:47 | I'm going to Ctrl-click on
this button and drag it over here.
| | 04:52 | For Action Segue, I'm going to select Modal,
and then I'm going to select our Segue like
| | 04:57 | this and the Identifier is
SegueToAddView, like that.
| | 05:08 | Presentation is just Default,
Transition is also Default.
| | 05:15 | Style is Modal, that's great.
| | 05:17 | Now, we're going to come down here
where we're going to connect our Controller.
| | 05:20 | I need to zoom back in apparently.
| | 05:22 | So, I'm going to Ctrl-drag from here down
to the Add View Controller, and we'll select
| | 05:28 | addAction and the cancelAction, and do the same
thing again and select cancelAction. That works.
| | 05:35 | Now, when I select this controller, we should
see those hooked up properly, they are, excellent.
| | 05:43 | The URL text field should come over here and
the Status Label will come over to this one here.
| | 05:54 | You can almost see that it's the second one.
It's not the top one, it's the second one.
| | 05:58 | And then finally, we want to drag this text
field, we want to drag it down into our
| | 06:06 | ViewController and where it says the
Outlet Delegate, we need to connect that up.
| | 06:10 | So, here we have our delegate outlet on the
text field is set to the Add View Controller,
| | 06:19 | and if we come down to the Add View Controller,
we see that our delegate is that text field.
| | 06:25 | So now, this should all be hooked up properly,
I'm going to press Command+S to Save, and
| | 06:30 | we're going to go ahead and
run it in the iPad Simulator.
| | 06:34 | I'm going to go ahead and
put us in Landscape mode.
| | 06:39 | We see there's our Plus button, I'll press that
button, and there is our Add RSS Feed Controller.
| | 06:45 | We can just type something in here, I'll
put in wired.com and press the Add button, and
| | 06:52 | there is our Wired Feed, and
you see that's all working.
| | 06:57 | I can come back here again, I can add another one
say arstechnica.com, and I can press the Done button.
| | 07:05 | All of this works exactly like it does now
in the iPhone version, and you notice we did
| | 07:10 | no coding at all for this.
| | 07:12 | All we did was hook up this new
View Controller to the exact same code.
| | 07:17 | So, this is a lot less work because we're
able to leverage most of the work we've already
| | 07:22 | done for the iPhone Form Factor.
| | 07:25 | The Universal application model allows us
to take advantage of two different platforms,
| | 07:29 | two different sizes of screens, two
different UIs, all with the same code base.
| | 07:34 | We now have a working storyboard interface
that we can use for the Modal dialog box.
| | 07:39 | With storyboard, this whole
process becomes a whole lot easier.
| | 07:43 | All we really need to do is set up the View
Controller in the storyboard and connect it
| | 07:47 | to our existing classes and then it just works.
| | 07:50 | So, congratulations. You now have a fully-functional RSS
Reader that runs in both the phone and tablet form factors.
| | Collapse this transcript |
|
|
ConclusionGoodbye| 00:00 | Thank you for watching iOS SDK and SQLite:
Building Data-Driven Applications.
| | 00:05 | It has been a great experience for me putting
this course together, and I hope you've gained
| | 00:09 | some useful techniques for building
your own data-driven applications in iOS.
| | 00:13 | For more about the SQLite database engine
we used in this application, please see my
| | 00:18 | course SQLite 3 with PHP Essential Training
here on the lynda.com online training library.
| | 00:24 | If you need more information on programming for the iOS SDK,
check out iOS SDK Essential Training with Simon Allardice.
| | 00:31 | I look forward to seeing your
apps out there on the App Store.
| | Collapse this transcript |
|
|