WWDC2013 Session 705

Transcript

X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Silence ]
>> Good morning.
I'm Steve and I'm going to
talk today about what's new
in Foundation Networking.
So, Foundation Networking
gives you new API this year:
NSURLSession.
You might have heard this talked
about yesterday during the
multitasking APIs talk.
NSURLSession is a replacement
for NSURLConnection.
The big benefit it's going
to give you right off
the bat is out-of-process
background transfers.
It's a way of providing your
applications with the ability
to get work done when
you're not running.
It's available on iOS 7
but also in OS X Mavericks.
I'm mostly going to talk
about some enhancements
to the Foundation frameworks
around some specific
technologies, NSNetServices,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
single sign-on an iCloud
credential syncing.
So off the bat we
have, you know,
sort of a generic chart diagram
of where your application sits.
Your app sits on top of maybe
WebKit, MapKit, UIKit on iOS.
This also sits on top of
the Foundation Framework
which contains the
Cocoa container classes.
There are classes
for scheduling,
for file transfer
and networking.
Foundation sits on top of
CoreFoundation and CFNetwork.
Now, the reason there
is a separation there is
because a long time
ago, we have the support
in API called Carbon.
Nowadays, almost all the work
you guys do is going to be based
on the Foundation layer.
But the CoreFoundation/CFNetwork
layer is still there.
Lastly, networking on our
platform is built on top
of BSD sockets with
enhancements in the kernel
for our specific mobile needs.
In these different frameworks
at these different layers,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
there are different
APIs you can use.
BSD Networking uses BSD Sockets
and this is obviously
well known.
But on top of BSD
Sockets, the CoreFoundation
and CFNetwork layer gives
you access to a stream API
in CFStream which allows you
to access files streams
or sockets streams.
CFNetServices which allows you
to publish and subscribe to
or browse for Bonjour services.
And CFHTTPReadStream
which gives you a way
of issuing an HTTP request
and receiving the body
of the response as
the stream data.
Foundation though gives you some
nice Cocoa APIs on top of this.
NSStream is a direct
replacement for CFStream.
There's no need to use CFStream
if you're using NSStream.
NSNetServices just wraps
CFNetServices but it does
so in a delegate-based
Objective-C ARC compatible way.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But NSURLConnection is more
than just a single class.
NSURLConnection is a suite of
classes, a bunch of classes,
it's also the name of a class
which is a little
confusing, but it's true.
Now, today we're
going to talk about--
we're going to talk about
NSNetServices a little later,
but mostly I'm going to
talk about NSURLSession
and then NSURLConnection, how
they relate, why you're going
to use one over the other, and
why you're not going to use one.
So going back in time
for NSURLConnection it
describes both a technology
that is the suite of
classes that are necessary
for doing HTTP loading,
and it's a specific class
in that there is an instance of
a class called NSURLConnection.
These classes were originally
written for Safari back
at the dawn of the millennium.
And they were first
made available in 2003
which I think the same year we
introduced the Power Mac G5 back
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
when the bits all
went the right way.
[ laughter, applause ]
It provides URL resolution--
thank you.
It provides URL resolution
and loading.
What that means is when
you have an arbitrary URL
like a file URL, NSURLConnection
is going to be used to load data
out of a file URL or out of a
data URL, or you can connect
to an FTP site, but mostly HTTP
and HTTPS is what people
end up using it for.
If you had your own protocol
that you wanted to register,
you could register that
in your process using the
NSURLProtocol class.
So NSURLConnection as an API
encompasses all the loading
machinery for the
Foundation layer.
Configuration of an
NSURLConnection occurs
by tweaking the NSURLRequest
object.
There are a couple of properties
you can set on an NSURLRequest,
some headers maybe you can set,
and some policy decisions
you can set.
But by and large,
NSURLConnection works by looking
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
at the global state
of your process
and global configuration
options.
NSURLConnection also gives you
access to proxies automatically,
and it challenges
you for credentials
when an HTTP request is
challenged by a server.
So what this looks like is you
create an NSURLRequest object,
you create it with the URL.
You configure it how you want
and then you create an
NSURLConnection object.
This object represents the
sort of the transient of state
of the request as
it's being made.
You create the connection
with a delegate
which receives information about
the load as it's occurring.
NSURLConnection then
looks in the global state.
It captures the cookie
store, cache, credentials
and any protocols that are
loaded in your process.
And it produces an NSURLResponse
Meta-Data object, and zero
or more NSData objects
which represent the payload
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of what you're getting.
So in your process, you have
some networking needs, right?
And you're just going to keep
doing this over and over.
You're going to create
a request,
you create a connection,
bind it to your delegate,
and the framework is going to
bind to the global storage over
and over and over to produce
these response objects.
So a problem is if
another framework
in your application also needs
to use this global state,
it ends up using and overriding
the same global state.
You can't both have your
own protocol handler
as you can't both have your
own global cache objects.
So the first thing NSURLSession
does is it continues the process
of having confusion over
whether it's the name
of a class or a technology.
It's consistent.
[laughter] I know
that's important.
So it replaces NSURLConnection
as a technology,
but it preserves
almost all of it.
So NSURLConnection as a class
goes away and to be replaced
by an NSURLSession class,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and NSURLSession has some
additional classes associated
with it.
NSURLSession is the
configurable container
for putting network
requests in to.
It gives you better
HTTP options based
on feedback we've
gotten over the year.
So I want to be able to this.
Well, you couldn't
before, now you can.
It gives you access
to the storage objects
on a per session basis.
So you can have private
storage objects.
It improves authentication
handling
by having an explicit
authentication challenge
mechanism for connection-based
requests.
In NSURLConnection, when
a request was challenged
by a server to authenticate
against a pipe,
for instance an NTLM server,
the challenge would come back
for an arbitrary request.
You wouldn't know necessarily
which request would
get that challenge.
Now, challenges for
connection-based
of are sent directly to
the session delegate.
And I have this rich delegate
model which is kind of--
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
weird way of saying this,
but NSURLConnection had some
asynchronous convenience
routines, but the
problem with that is
that they wouldn't
use any delegates.
So either the asynchronous
routine would work
or it would fail, now you
can have a hybrid approach
where there is an asynchronous
convenience routine,
but it can also use your
delegate to do auth.
Session gives you uploads and
downloads to the filesystem,
that's sort of the preferred
way to do uploads and downloads.
There's sort of a
policy shift in Session
in that the configuration
goes in via Session instead
of NSURLRequest and we try and
separate the body of a request,
that is if you're doing
a post of an image,
from the request
envelope itself,
it's sort of like you don't
want to have a post star,
you want to have an envelope.
We've always had the separation
of that response meta data
from the actual payload itself.
So just trying to make this
API consistent between request,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
response, payload, payload.
And NSURLSession
gives you access
to out-of-process
uploads and downloads.
The nice thing about
that is it's just a
configuration option.
When you create a session, you
specify, "Well, I want request
that go into the session,
could be done on a background."
What it allows us
to do though is
when your process is no longer
running, it's been suspended,
we can continue your download in
the background taking advantage
of everything we've learned
about the user's network use
and their battery life.
Using UIKit on iOS 7, we
key in, we take advantage
of the new multitasking APIs
so that your application can
have updated its interface
and provide a better
experience for the user.
You're going to download
your content
and have it available in the UI.
So this looks like,
well, it's a big object,
a big square object
called NSURLSession.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And you create a session
with a configuration object.
This is where you're going
to specify any HTTP options
and the various storage objects.
And you create a
session with a delegate.
Delegate gets called on a
sort of per request basis.
When you throw a request into a
session, you get a response out
and you keep using the
same session over and over.
So as Session is an
API, there are a bunch
of additional classes
you need to know about,
not nearly a bunch
but some more.
OK, so the first one is
the configuration object.
This is basically a
properties dictionary
of how you want the session
to behave, the task object
which really is the
replacement for connection.
So you can think about
Session is this larger ball,
and Connection goes away and
these taske objects get created
on a per request basis.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
There is a delegate
protocol, the delegate object,
that you're going to
create to bind your session,
and then the session
object itself
which is a factory
for creating tasks.
You always create
one task per request.
So this is a classic
NSURLConnection example.
You create a delegate
object and a URL.
I keep using
setAllowsCellularAccess during
this talk as an example
of a property
that you might tweak
on a request.
So in this case, you
create an NSURLRequest
and then you modify
the request to say,
"I don't want this request
to go over cellular."
You create an NSURLConnection
object using the class method
connectionWithRequest, you
bind it to your delegate.
Your delegate then gets called
with didReceiveResponse,
didReceiveData, didFinishLoading
referencing this
connection object.
Now in NSURLSession, it's
pretty much the same,
but there is more configuration
upfront but less work later on.
You create a delegate conforming
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to the session delegate
protocol.
You create a configuration
object.
There is a bunch
of class methods
for creating configuration
objects based
on what you're going to
try and do with them.
In this case the
defaultSessionConfiguration
object actually gives
you the same sort
of configuration options that
NSURLConnection would see.
So if you had created this task
with an NSURLConnection instead,
you would have sort
of the same behaviors.
But instead of modifying the
request to say AllowCellular,
all the requests that
go into this session
that I create are going
to not use cellular.
So you create a session, you
bind it through a delegate
and you give it a queue
where the messages
for your delegate are
going to be received.
You don't want to
block in your queue.
All the callouts to you
are sort of asynchronous
or they're intended
to be asynchronous
or either informational or
they'll be a completion handler.
If you use the session
once you get all set up,
you create a URL, and
then you can use a
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
dataTaskWithHTTPGetRequest
which returns
to you an NSURLSession
data task object.
Actually, I want to talk
about one more thing here.
The data task object I've
mentioned NSURLSession task,
NSURLSessionDataTask is a
subclass of NSURLSession task.
And I'm going to talk about
that a little bit more, sorry.
Right, so
NSURLSessionConfiguration.
NSURLRequest did not have a
lot of configuration options.
So now we're adding
support to subclass
and set your own private cache
both using credential storage.
We maintain the ability
to set the cell usage flag
and network service
type from NSURLRequest.
We give users the ability
to tweak the maximum number
of connections through a host.
In NSURLConnection, behind the
scenes there is this global pool
of sockets that have been opened
and we come up with some number
of connections based on
internet standard usage
for how many connections
we want to open to a host.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But what we've heard is that
people want to tweak that.
They say "I really only want
one connection to a host ever.
I'd like all request to go
up in this one connection."
So now you can do that.
We have the resource
and network timeouts.
This is nice because while
NSURLRequest has an implicit
timeout when you create one,
or you can set an explicit one.
That timeout is for
network transfers.
If you're getting a long-lived
request and maybe you want
to get a request, you want to
get the bytes out of request,
but only one byte is
coming in at a time,
if you don't get any bytes
for that network timeout,
then the request times out.
So you can imagine doing a
get where it might take a week
to come in and never timing out.
So there's a new timeout,
the resource time out.
If I can't get this entire
request within this timeout,
then I want it to cancel.
This is particularly important
if you create a background
transfer.
I want this file,
but if I can't get it
in three days, forget about it.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I'd rather hear that it failed.
You can now set the minimum and
maximum TLS levels to, you know,
ratchet down security or open it
up for your particular server.
You have access to the HTTP
proxy dictionary directly
on a per session basis.
This is the same dictionary you
would get from systemconfig,
but now you can modify it if
you had special proxy needs
within your application
and we're going to use
that proxy dictionary
for all requests
that travel through the session.
We have some of the same
options on request as far
as cookie handling is
concerned, whether you want
to apply cookies as they go out,
but you can also specify the
cookie acceptance policy.
HTTP pipelining is something you
can turn on and off at this--
on a per session level.
And you can set additional
headers
that will be applied
to any request.
If a header is already
present, we don't override it.
But if you wanted to
set the user agent
for any outgoing request,
you could do that here.
Lastly, protocol handlers are
kept on a per session basis.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
There is still the
global protocol list.
But if you have your own
protocol, it would be better
to bind it to a particular
session and use that session
and expect to get that protocol.
So there are some
factory constructors
for creating sessions.
The default configuration sort
of captures the global state
that NSURLConnection sees.
There is a factory to create an
ephemeral session configuration.
This is basically the same as
a default configuration except
that it initializes the storage
objects to be in-memory only.
This is how you would implement
private browsing for instance.
And there's the background
session configuration
which creates a configuration
object
that references an identifier.
That identifier is what you
use to reconnect this session
to the background work.
Configuration objects
are always mutable
and they're copied
whenever they get used.
So when you create
a session with one,
it'll create a copy of that.
And while you can get to
the configuration inside
of a session, you
can't modify it.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So the task object,
this is the thing
that actually replaces
NSURLConnection in the class.
And it represents, you know,
the state of a loading resource.
When you look at it in the
header, the immediate thing
that comes out is that it
has a lot of properties.
The properties that previously
had been reported via delegates
are also available through
the task object itself.
So you can look at a
task object and find
out how many bytes
have been transferred,
how many bytes are
expected to be transferred.
It offers the same
cancel, suspend and resume
that NSURLConnection does.
There are two subclasses for
data task and upload task.
This is really done just
as sort of syntactic sugar
so that we understand that
there are different types
of work that's going on.
And in particular, you can't
schedule background data task.
You can schedule background
upload or download tasks.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Download tasks don't
really offer anything
over the base class task except
for this
cancelByProducingResumeData.
If somebody hits
pause in the UI,
you want to stop the
background transfer
or foreground transfer
for that matter.
You're given this
resume data blob.
Later on, your app can go ahead
and create a download and bind
to this resume data and we'll
continue where we left off.
The delegate that you create the
session with has messages keyed
to the specific task type.
So all tasks can finish loading,
but only data tasks
receive incremental data,
and only download tasks receive
that final URL where
the file is.
So the class hierarchy,
you know, looks like this.
You've got a session task
with these methods on it.
I think there's a status method,
but there are also properties
in there that you can look at.
DataTask is a subclass
of SessionTask
and UploadTask is a
subclass of DataTask.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Any task can have
an upload payload,
including a download task
which is weird when you think
about it but it's true.
You can do a GET for a resource
and have it have body data.
It's not my protocol.
[ laughter ]
So the DownloadTask subclass
also has this extra method
cancelByProducingResumeData.
The delegate objects that
you create your session with,
you create a single
delegate object, right?
So each session has one
pointer to your delegate object.
There are-- different
task types are broken
out into different protocols,
but you can just implement
them all within one class.
The delegate itself is strongly
referenced by the session.
And in order to break
that cycle,
you have to invalidate
a session.
When you invalidate a session,
all the work inside the session
gets canceled or finished,
and then the session
did finish--
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
or sorry the session
did invalidate,
delegate method gets called.
At that point we
release your delegate.
Now a Delegate message
may block loading.
Some of them are informational
like didReceiveData,
gives you a data blob and
then goes back to work.
Some of them, like
didReceiveResponse,
require you to return something.
In NSURLConnection, you had to
return something immediately,
but in the SessionDelegate
model,
you're given a completion
handler.
Then you invoke that completion
handler to continue the load.
So it's OK to schedule a session
on say the main queue as long
as you know that the
work you need to do needs
to be done via this
completion handler.
SessionDelegate then is
sort of the overarching--
a session should implement this.
I mentioned way earlier
that there's an authentication
challenge specific
to connection types
as opposed to request.
Those come in through the URL
session didReceiveAuthentication
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Challenge:completionHandler
delegate method.
This is going to be for
connection-based auth
like in NTLM also for SSL auth
like here's the server
certificate,
or if the server requires
a certificate from you.
We implicitly handle
Kerberos on both iOS 7
and with OS X Mavericks.
So you should be aware
that it's happening
that you might have a Kerberos
authenticated connection,
but you actually won't hear
about it through this delegate.
The other delegate
method that we care
about is
didBecomeInvalidWithError
and this is where-- when
you've invalidated the session,
this is the method that gets
called before your delegate
is released.
So data delegates are
interesting because this is
for doing incremental
loading of data.
These are basically-- some
of these are optional, right?
So if you don't care about
redirections, you don't have
to implement
willPerformHTTPRedirection.
If you care whether you want
to do something different
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
for redirection, then
you implement this
and it gives you
the new request.
This is where you're
going to go to
and call the completion handler
with either that new request,
the request where you'd
rather it go to or nil.
If you pass nil, then
we treat the request--
sorry, we treat the response
that specifies the redirection
as the data you're getting.
Sometimes you want to
do an HTTP Get request,
you get a redirection response,
but you'd rather have the body
of the response rather
than the new location,
so that's how you
would implement that.
When you have a request based
authentication challenge,
they come in through the
didReceiveAuthentication
Challenge, the task
didReceiveAuthentication
Challenge.
This is where you create a
credential for basic digest auth
or any sort of proxy auth
on a per request basis.
As the request is uploading
data through a server via POST,
we periodically call this
didSendBodyData delegate method
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
if you implement it.
This data is-- the parameters
here are actually mirrored
in the task object itself.
So if you look at
the task object,
you'll see these
things being modified,
and you don't necessarily
need to implement this.
needsNewBodyStream
needs some explanation.
This was added to
NSURLConnection because when--
it is a carryover from
NSURLConnection delegate,
because when we're
doing an HTTP post,
sometimes the server is going
to come back with a challenge
or some sort of server error
that we can recover from.
We could resend this request.
But because that-- because
streams are not rewindable,
we want it to have the
data to send them again.
So we will call your
needsNewBodyStream
and your needsNewBodyStream
delegate to ask for a new stream
so we could resend the request.
If you didn't implement this,
then the request would fail
and you'd have a confused user.
The problem is, you know,
when you're doing your testing
in-house, this never gets called
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
because everything
is working correctly.
But out in the field, you know,
there's one in a million
chance this is going to happen,
but one in a million chances
happen 9 times out of 10,
and so your users
will be confused.
[ laughter ]
needsNewBodyStream
must be implemented
if the upload you're
doing is from a stream.
Sessions lets you create and
upload from a file though.
So if you are implementing
uploads via the filesystem,
you don't need to
implement needsNewBodyStream.
If you're doing an
upload via a stream,
we require that you
implement it.
Lastly, all tasks
finish with error.
NSURLConnection had
didFinishLoading
and didFailWithError.
NSURLSession just has
didCompleteWithError,
error will be nil if
there was no error.
Note that the error that you're
sent here is a transmission
error, not a server error.
If the server responded
with 404,
as far as we're concerned
we did our job,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
we sent you request
and got a response.
The only errors you're
going to get here are errors
that are specific to, "We
couldn't find the host,"
or "the connection was lost,"
or some horrible thing happened.
Data tasks have a couple
of specific data-related
delegate messages.
First one is didReceiveResponse.
Now, this is interesting because
when the server gives you a
header back and it
starts sending body data,
you might decide that the
response you got doesn't need
to go to memory, you just
want it to go to disk.
You can use didReceiveResponse
now to specify
that this data task that
I'm about to get data for,
I'd rather have the data
go directly to disk.
So you call the completion
handler
with a disposition
saying "become download.
" And the next delegate
message gets called to say,
"Your data task is now dead.
Here's a download task instead.
" And all the flow goes
through that download task.
As your resource is being loaded
from the network, if you kept it
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
as a data task, you'll
receive zero
and more didReceiveData
messages.
The NSData objects that you
get behind the scenes may be
stitched together from
many NSData objects.
So in the past, you would
just have gone [NSData bytes]
or bytePointer or whatever.
We would encourage you
to instead use the
enumerateByteRangesUsingBlock
API because we could give
you now a 4 Meg buffer.
We just downloaded your 4 Meg.
Internally, maybe that's cut
up into 50 different segments
and if you just ask for the
byte pointer, then we have
to flatten all that, and
that's not good for anyone.
Lastly, data task caching
- only task can be cached.
If you don't implement
willCacheResponse then we go
ahead and cache for you all
things being considered,
whether caching is
enabled in your session
and whether the resource
is cacheable
and whether the server
said to cache.
Note that this won't
necessarily get called
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
because there could
be something--
maybe the resource is too
big to fit in the cache
or maybe the server didn't
want it to be cached.
So we won't always call
your willCacheResponse.
So really, the only reason to
implement it is to say, "No,
I don't want to cache this
particular response type,"
or if you want to modify the
user info dictionary associated
with the cache response.
Download tasks always
download to a file.
We create the file for you.
We put the bytes into it
and then once the
download is complete,
we call
didFinishDownloadingToURL.
We give you a reference to a
file URL which you then need
to copy or move because
we're going to delete
that file shortly thereafter.
As that file is being
downloaded, we give you access
to the number of bytes
that have been transferred
for your progress use.
And if you created your download
task using the resume API,
then we're going to
call didResumeAtOffset.
One thing to note is that the
offset you're given here is the
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
real offset at which we
began resuming the download.
We may previously have
said, "We've downloaded 4K,"
but when you get to Resume,
we've decided "We're can
only trust 2K of that.
" So that number
may go backwards
from what you were
previously told.
So the session object itself.
There's a default session
object that you can use
and this shares the same
world as NSURLConnection.
As a matter of fact, the
configuration object inside
of a shared session, if you
look at its Cookie storage,
it's the same
as the NSURLConnection
Shared Cookie storage.
But you're going to create
your own private sessions
with private-- either
private configurations
or using the default
configuration.
But you're going to create
your own session usually.
When you're done
with your session,
you have to invalidate it,
and then that delegate message
didBecomeInvalidWithError is
called and then your
delegate will be released.
Session is the object
that creates data upload
and download tasks, but it also
provides these asynchronous
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
convenience APIs with
a various type of task.
And the nice thing about those
is that they give you access
to your delegates
authentication method.
It also lets you cancel them.
So NSURLConnection send
asynchronous request
as an existing API.
Once you fire that, you
have no control over it.
It's going to finish
or it's going to fail.
But with Session, you're
given a task object back.
You can monitor that task object
and you can cancel
it whenever you want.
So to create a session with
a custom configuration,
in this case, I'm going to
implement private browsing.
So we create a new configuration
object using the ephemeral
configuration delegate
or class method.
We create a session with
that configuration object.
Whatever the URL is, we throw it
into the session with data task,
with an HTTP get request,
in this case I'm using the
asynchronous convenience API
which will invoke my block
with the data once
the data is resolved.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If I wanted to further
customize this,
I would have added whatever
configuration I wanted on top
of the configuration object
that I created initially.
So to create a DataTask,
it's sort of just
like NSURLConnection, you
throw an NSURLRequest,
they are convenience
APIs for throwing just
for all URLs into it as well.
The convenience API version
of this takes an
additional completion handler
and we are just going to
invoke the completion handler.
We invoke it on the same
queue as your delegate
if you had created the
session with the delegate.
Upload tasks can be
explicitly created from a file
or they can be explicitly
created from a data object
or they can be implicitly
created with a stream.
When you do
uploadTaskWithStreamedRequest,
we take your NSURLRequest
and even though NSURLRequest
has an existing API
for setting a body, if you
throw it in to a session
through
uploadTaskWithStreamedRequest,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
we ignore the body.
We will always call your
needsNewBodyStream method
to get a stream when we need
to trasnfer that request.
There is no convenience routine
for that model of upload task,
but there are convenience
routines for upload
from file or upload from data.
Download tasks are similar, you
create them with an NSURLRequest
which identifies, you know,
whatever resource you want
to get off the network.
Or you create it
with the resume data
that you previously had captured
by cancelling with resume data.
There is a convenience routine
for both of those as well.
It calls this block
with the location
of the file that we downloaded.
Again, you have to
copy or move this file
because we're going
to delete it.
If there was a connection
error during a transfer
and we're going to be
able to resume that,
so let's say your user
walked out of Wi-Fi range
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and we lost the connection,
we might return
to you a connection error.
In the user info of that error,
there is this key
NSURLSessionDownloadTask resume
data where we will have
squirreled away resume data
so that you can restart
that transfer later.
So background transfers, this
is where we're going to talk
about the ability to do work
while you're not running.
You have to use the delegate
model for background transfers
and we only support
HTTP and HTTPS,
obviously it doesn't
make any sense
to do a background
transfer from a file URL.
You use exactly the
same delegates as you do
for upload and download tasks.
We have to make some assumptions
though while you're running.
So we don't bother calling you
back to take a redirection.
We're going to assume that
any redirection should always
be taken.
And there's one property
I want to call
out on the configuration object
which is this discretionary
property.
What this tells us is that
when we're doing a background
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
transfer for a session
that's configured using this
discretionary property
being true,
is that we can take advantage
of the state of the machine
to prioritize this
download to only do it
when things are really good.
Typically, that's going to mean
I'm on Wi-Fi and I'm plugged in.
If you're in the foreground and
you don't specify this property,
your download actually has
a high priority, you know,
your user is sitting there,
you've created a download task
and you're going to watch
this thing make progress.
So we want the user
experience there to be--
that the work is
being done right now.
If you started your
download task when you were
in the background though,
we assume that discretionary
is true,
we're going to prioritize
it as if, you know,
this session were created
with the discretionary
flag being set to true.
So if you're a foreground app
and you don't care how
long it's going to take
for this resource to come in.
I don't want to say, you
don't care, I mean you care,
but you don't care that much.
You set discretionary to be true
and we're going to do our best
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to prioritize it given the
constraints of the network
and the power is-- the
current state of power.
So what this looks like then
is that it creates session
with an identifier, a
background session configuration
with an identifier.
This causes a daemon
to come into existence.
You create your download
object, your download task
and that starts the
work happening
in the background daemon.
While your app is running,
it receives progress updates,
I downloaded this many bytes.
But if your app stops running,
the work continues
off in the daemon.
And when the daemon is
done downloading your file
for you UIKit application,
we're going to re-launch you,
you re-create your session
which reconnects to the daemon
and then the daemon
can tell you, "Hey,
you're completed and
here is the file.
" So, while your app is running,
you create a background pass,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you're going to receive
these update messages.
If you're a UIKit app on iOS
7, when you're not running,
we're going to have to launch
you if we need credentials
or if all the requests that
you have enqueued are complete
and we're going to call the
application handle events
for background URL session
completion handler delegate--
application delegate.
What you do then is
reconnect the session
to the background daemon.
You're going to receive
all the delegate messages
for the outstanding tasks
and then when you're done,
you call that completion
handler to update your snapshot.
When you do reconnect, you can
probe, you can ask the session
for any outstanding tasks.
So you create a background
session configuration,
you create a session
then you call
getTasksWithCompletionHandler
which goes off to the daemon,
gathers up all the task
objects and sends them back
so you can interrogate them.
But you will receive
delegate messages for a task.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You don't have to call the
taskWithCompletionHandler.
All right, so Dan is going to
come up and do a demo of this.
>> Thank you, Steve.
All right, good morning
everyone.
So today, I'm going to
do a demo of an iOS app
that uses the NSURLSession API
to do a background transfer.
So this app does two
very simple things.
It downloads an image
in the background
and it can de-reference
an old pointer.
So you should only be doing one
of those things in your apps.
So, I'm going to hit this Start
button and now you can see
that our download
is making progress.
So now, as it gets us
[inaudible] progress,
I'm going to suspend the app so
now the app is no longer running
but our background daemon is
actually doing the download.
So if we look on this
app switcher then
when the download finishes,
we'll re-launch the app
and an image will appear.
[ Applause ]
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, now just to show you
again, we can start a download
and this time, instead of
suspending the app after it goes
for a little while, I'm actually
going to hit this Crash button
and now that they referenced
an old pointer and it crashed.
But even though the app
crashed and there's no--
it's no longer even a process,
we're still downloading
that file in the background.
So then the next time we go back
to our app, the file is there.
[ Applause ]
So, now I'm going to
walk you through the code
and show you how
I wrote this app.
So, first of all, this is--
here is where we
create our NSURLSession
and the magic here is where we
created the background session
configuration and pass in
an identifier and again,
this identifier is so that
when you create this session
on subsequent launches, you'll
be reconnected to the session
that already exist
in the daemons.
So, I do this inside
of a dispatch once here
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to emphasize the fact that
you should only be creating a
session with a given
identifier once.
And if you want to create
multiple sessions that have--
that do background work,
that's perfectly fine,
but they each need
their own identifiers.
So now that we have our session,
we can create downloads.
So here's how you would create a
download task with that session
and this is exactly like how
you would create a download task
with an in-process
session as well.
So you create an NSURL request
with the URL that you want
to download and this is--
should also be familiar
from using NSURLConnection.
And then we use the
downloadTaskWithRequest method
to create a download
task in that session.
This is a progress callback for
the download task, it tells--
it can-- and it can
give you information
about how many bytes have
been written to disks so far.
And in our Apple, we use this
to update a progress view
by diving total bytes written
by total bytes expected to write
and that gives us our
download progress.
So the fraction, we use that
to update that progress wheel.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
This is the--
didFinishDownloadingToURL
delegate message
and this happens when we
finish writing all the bytes
of the download to disk.
And as we mentioned before, you
need to link or copy this file
into a more permanent
location because as--
after these returns, we're going
to unlink the temporary
file that we downloaded.
And we all-- and since this
was an image, it made sense
to display it so
we created an image
and displayed it an image view.
And finally, this is
task didCompleteWithError
and this is the last callback
you will get pertaining
to a particular task and
as Steve mentioned before,
if the connections exceeded
and the downloads exceeded,
this error will be nil and if
there was a transmission error,
the error will be non-nil.
And we also here call this call
completion handler a finished
method and I'll get
back what that--
to what that method
does a little bit.
So this leads us though to our--
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to one more additional
difference
between in-process sessions and
background sessions which is
that in a background session,
we have support in UIKit
to launch you in the
background when download--
when all the downloads
in the session finished
or when a particular task
needs authentication.
So-- and this is why the
application handle events
for background URL session
completion handler method.
So, when this is called, you'll
want to reconnect to the session
with a given identifier.
So in the case of this app, we
actually created our session
when our view controller
was initialized and which--
that happened when
application did finish launching
with options was called.
So at this point, we'll
already actually be connected
to our session.
And then we have this completion
handler and we want to--
what this completion
handler does is it allows us
to take a snapshot of
your UI and display it
in the app switcher
and it also tells us
that we can suspend your app.
So it's very important
to call this
when you're done handling
all the events pertaining
to the session.
So we only want to call this
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
when we're done handling
the events.
So for right now, we're going
to save it in a property
on the app delegate and
we'll invoke it later.
And it's important to note that
this has to be a copy property
since there's a block
and you have to--
blocks have to be
copied when saved.
So let's go back to our
download view controller
and I mentioned this
call completion handler
if finished method before.
So let's see what that does.
We called it when the task
finished and now what we want
to do is we want to
check if it's time
to call our completion handler.
And we only want to that once we
know we're done handling all the
events for all the
task that finish
when your app is
in the background.
So, in this case, we
only did one download,
but this is what you
would do if you--
if your app did multiple
tasks in the background
which is perfectly reasonable.
We used the sessions get task
with completion handler method
and this will give us back
a raise of all our data,
the upload and download tasks
and it won't give you back any
tasks that have been completed.
So if all these arrays
are empty,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that means you know you're
done handling all the events
and we can call our
completion handler.
So here we get our app delegate.
We see if we have a
completion handler
and if we do, we invoke it.
And it's-- that's how you
use background uploads
and downloads.
Back to you Steve.
[ Applause ]
Thank you.
Thanks Dan.
OK, so why do we want
to use NSURLSession
versus NSURLConnection?
NSURLSession again
provides you with access
to connection based auth which
is something NSURLConnection did
but only by targeting
specific requests.
Session gives you better
HTTP configuration options
that people had been
asking for, for years.
These are available through
the NSURL configuration object
that you create your
session with.
The ability to create private
subclassable storage objects is
something you couldn't
do before.
You couldn't create a cookie
storage subclass and have
that available to
the system views.
You could create an
NSURL cache subclass
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
but then you would set it to
be the default for the system
and all other processes would
end up using it as well--
sorry, all other frameworks
within your process.
These are always per
process properties.
Very importantly,
Session is how you get
to add a process
background transfers both
for uploads and for downloads.
It's also going to be the
baseline that we're going
to build any future
APIs on top of.
Session gives us a context
that we didn't have before,
before the context
was your process.
Now within your process,
there's a lot
of different things going
on, Session is how we're able
to compartmentalize that
within your application.
So we spend a lot of time
talking about session.
There are some other
enhancements that I want
to mention that are new
in iOS 7 and Mavericks.
NSNetServices is an existing
API that allows you to browse
or discover and connect to
Bonjour Publish Services.
It also allows you to
publish Bonjour Services.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You do this by creating an
NSNetServices describing the
type of service you
want to connect to
and you call this get input
output stream message method.
The streams you get back, you
open them and at that point,
the resolution occurs, once the
stream is successfully open,
that means you have a raw socket
that you can do your own
protocol framing on top of.
There's a new property in
iOS 7 includesPeerToPeer.
When you specify this
property in your NSNetServices
when you perform a browse,
it will also browse on peer
to peer Wi-Fi and Bluetooth
networks automatically.
The peer to peer Wi-Fi
is new support in iOS 7.
There's also a new option
for publishing a service
which automatically creates
a listening socket for you.
So when you create your
NSNetService to publish it,
you need to specify this
new option NSNetService
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
ListenForConnections.
We'll listen on IPv4 and
IPv6 for incoming connections
and when a remote
connects to that socket,
we will call this
new delegate method,
netServiceDidAcceptConnection
with input stream output stream.
You open your streams and now
you have a raw protocol stream
to frame your own messages
onto the remote site.
Authentication in iOS
7 gets single sign-on.
Kerberos Authentication
is automatically handled
by device managers that
have installed a profile
on your device.
You can specify which
connect type--
what type of connection
should be authenticated based
on the URL or by application.
And we handle this implicitly
for outgoing connections.
You need to know that it's
happening for your connections
in your app, but you won't
necessarily need to have
to do anything special about it.
There is an-- a session,
I think it was yesterday
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
"Extending your Apps for
Enterprising and Education Use"
which you should check into
to find that more about how
to configure a system
and how to respond--
well, you don't have to respond,
how to deal with Kerberos
authenticated connections.
The final API enhancement
in Foundation Networking is
iCloud Credential Syncing.
When you create an
NSURL credential
which represents the
username and password
or client certificate, you're
presenting to the remote site.
You specify a persistence type
for it so that we can keep track
of it in the
NSURLCredentialStorage.
There's a new persistence
type called synchronisable.
When you create a credential
with this persistence type,
it automatically could
sync through iCloud
to all devices registered
for that iCloud account.
In order for this to work,
we have to have a new API
to actually remove the
credential as well.
So there's this new
remove credential
for protection space API
on NSURLCredentialStorage.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You invoke this using a
dictionary of properties
and the key you want to look
for is the
NSURLCredentialStorage
RemoveSynchronizableCredentials.
So, [inaudible], most of what
we talked about is NSURLSession.
We do think this is going to
be a good way for you guys
to build your networking
applications in iOS 7
and OS X Mavericks
going forward.
It replaces NSURLConnection,
but I want to point
out that NSURLConnection
doesn't go away.
It's still there and you
can use your existing code,
doesn't break but NSURLSession
is sort of the preferred way
to do these types
of request later on.
It gives you a lot
more customization
over NSURLConnection.
And it very importantly
allows you to get work done
when you're not running.
NSNetServices adds peer to
peer support both browsing
and publishing and it
has a new listening API
so that you can create
services and publish it
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
on your local network and
NSURLAuthentication is enhanced
with Kerberos single sign-on
and iCloud credential syncing.
OK, so Paul Danbold is our Core
OS Technologies Evangelist,
he is happy to field
any questions.
The Foundation Class
Reference is being updated
with documentation
for NSURLSession
and the Developer
Forums will be available
to answer your questions,
they will hang
out there and answer questions.
There's some other sessions I
want to point you out though,
yesterday was this Managing
Apple Devices session,
that and the Extending
Your Apps for Enterprise
and Education Use
session is where you learn
about Kerberos Authentication
and how that's handled.
Yesterday's talk on What's New
with Multitasking
is very important
and relevant to this discussion.
Dave Chan showed how to use
NSURLSession in response
to background notifications
in order to get work done
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in response to, you know,
a server pushed token.
Nearby Networking with Multipeer
Connectivity follows this
session and we're going
to talk there about--
more about how to do--
use NSNetService to create
server use and publish service.
And then sounds like What's New
in State Restoration is relevant
because we want to know when
your application is re-launched,
how to update your snapshot
and have a nice live
experience for the user.
So thank you.
[ Applause ]
[ Silence ]
sma dv