Transcript
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Silence ]
[ Applause ]
>> Good afternoon.
Thank you.
Welcome to what they tell me is
Session 416, Modern Frameworks.
Today I'm going to be talking a
lot about mostly the mechanics
of setting up modern
frameworks on iOS,
some of the API design
principles that we use at Apple
and that we recommend
that you guys use.
If you were here for Andy and
Collin's talk just before this,
they really delved into a lot
of the deep architectural
components of how
to build your application.
I'm going to cover some
of the higher level stuff
and then we'll go through a
quick tour of Xcode and talk
about how some of these things
get build directly into your app
and just some of the mechanics
of getting all of that set up.
So, how many of you have
actually used static libraries
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, how many of you have
actually used static libraries
in your application that
you get from a third party?
That's everyone.
Okay. If you're doing
it without one
of those things you're probably
using the target management
mechanisms in Xcode.
Right? You download a
bunch of Source Code files,
you put them someplace on your
drive, you hope you remember
where you left them, and you're
messing around with this,
and you're shifting things
in and out of targets,
you may be even taking
them wholesale
and dumping them directly
into your application
so now you've got six or
seven copies across all
of the applications
you're working on.
Things like that.
The next step up from this
is the Static Library, right?
The Cocoa Touch Static Library.
It's a .a, you still
have to put it someplace.
You're probably checking
it into your sources.
The .a is separate
from the header,
so you need to put
the header someplace.
You can't carry assets along
with it so you don't get
to do things like have
a bunch of nice pictures
or customized controls
and things
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
or customized controls
and things
like that in your application.
So, this is good but not great.
In iOS 8 -- so actually let
me back up for a second,
OS X developers,
you guys are here?
All six of you?
No? Okay. How many of you have
actually shipped a framework?
Wow, okay, a good number.
Great. iOS users you
can do it now, too.
There are some restrictions,
though.
[ Applause ]
So we'll talk about that.
One of the reasons that you're
going to use frameworks is
to be able to package
absolutely everything
up that you need
as a single unit.
Right? So this is going
to include headers,
it's going to include
images, asset catalogs.
You name it, throw it
in the framework it goes
along as a single unit.
You'll be able to put
those frameworks someplace
on your drive, keep
track of them.
Xcode will take care
of moving those things
into the right place on
your application builds.
Right? There are a lot
of really neat features
that have been built in for
iOS 8, especially in the face
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that have been built in for
iOS 8, especially in the face
of the some of the new features,
to be able to make the workflow
for frameworks really
effective for your iOS 8 apps.
Why do you want to
use frameworks?
Well, all of the packaging
stuff is really nice,
but all of the features that are
in iOS 8 take advantage
of frameworks.
So the first thing you probably
want to try using is live views,
they're the live rendering of
views in Interface Builder.
So if you're putting your
things into a framework all
of that custom-rendering,
all of that custom-drawing
becomes available
to Interface Builder for
use directly in Xcode
as you're editing your
NIBs and your storyboards.
I guess that shows my age,
they're all storyboards
now, right?
It's not NIBs anymore.
If you're using extensions,
right, you're going to want
to use frameworks because you're
going to be able to share a code
between the extension and the
application without having
to duplicate stuff
in each place.
Right? So you won't be
putting things in the target
for the extension and the target
for the app, you'll put it
in the framework
and link the two
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in the framework
and link the two
against the framework itself.
If you write multiple
applications
and you're using either a third
party framework you're getting
from someplace else or you
have a bunch of your own tried
and true tested code that you
like, all of your categories
on NS string -- because, you
know, who does that, but, okay,
categories on a NS string,
and things like that.
If you're sharing code between
multiple applications you're
going to want to use frameworks.
And obviously also
if you're going
to be publishing a framework, so
if you're going to be making all
of your code available as a
framework, you're going to want
to be able to package that up
in a single distributable thing
that everybody can download and
use and you can version those.
So, let's talk a little
bit about getting set up.
Xcode makes it really easy
to create a new framework.
There's a new template right
inside of the new file dialogue
or the new project dialogue
that says Cocoa Touch Framework.
Right? So you can
just go click on that,
click on the Cocoa
Touch Framework,
it's going to walk you
through the same form,
give it a bundle ID,
things like that.
Save it someplace,
you're off to the races.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Save it someplace,
you're off to the races.
This also works if you
have an existing project
and you create a
new target and one
of the new target templates is
a Cocoa Touch Framework, right.
So what goes in it?
Once you decide you're
going to use Frameworks,
what are you going
to put in there?
If you're using live views, if
you're interested in frameworks
for live views, you're going to
want to put the views in there,
and the controls, and
anything else that goes along
with giving your app
that custom appearance.
Right, so these are going
to be all the assets,
in the views it'll be all the
drawing code, the controls,
again, it'll be assets,
things like that.
They all go in the framework.
That way, now, when you link it
for using with your application
or giving it out to all of your
friends and neighbors and trying
to use it within Xcode for IB,
all that stuff just
starts to work.
What goes in if you're
using extensions?
Well you're going to put
all of the same stuff
that you would've put in
for live views, the views
and controls and the
custom appearance
because your extension
probably has all of that.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
because your extension
probably has all of that.
You're also going
to put in things
like view controller
classes, right?
The custom view controllers
that you're writing and you want
to make available as
API to everyone else
or to your extension and to your
application, those are going
to go in there, as well.
You're probably also going
to put the service API
that you're using down there.
So if you're doing network
code, things like that,
all of that stuff's
going to go down there.
Resources, again, assets,
configuration files
that might be particular to
your service go down there.
Anything that goes in common
to your extension
in your application.
So, that's also true
if you're going
to be using framework contents
between multiple applications.
Right? So you've two or three
applications in the store,
your users are going to go ahead
and download those
applications from the store.
You're going to want to put
all of the stuff in common.
Right? The idea here
is you want to be able
to change the Source Code
once, build a new version
of your framework,
make it available
to all your applications
on your machines so that
when you ship them off
to the store all those
changes get picked up.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to the store all those
changes get picked up.
And when you're publishing
frameworks,
again, it's the same thing.
So, really what you're
going to wind
up doing is refactoring
your application using a lot
of the architectural things that
Andy and Collin were talking
about in the previous
hour, pulling those
into an organizational
unit, like the framework,
in order make your
development a lot easier.
The app right now, if you're
using extensions, for instance,
you've got an application,
it's going to have two parts.
It's going to have the
app and the extension.
That nifty little building
brick there is the extension.
Right, so they sit next to each
other inside the application
bundle, you can think
of the extension
as basically a little
tiny application.
All right?
You've got a bunch of
Source Code in there
and it probably shares some
Source Code and Swift files,
maybe some Objective-C files.
Those things are all going to
be part of the target, right?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Those things are all going to
be part of the target, right?
Rather than have the
object code generated
by those things getting
duplicated into each
of those targets, what you'll
do is just pull the common stuff
down into the framework
and link the application
and the extension
against the framework.
How do you decide what goes
down there as far as code goes?
If it looks like it's showing
up more than once in a target
or more than once in your
application, it probably goes
in a framework, right.
We do this at Apple.
We look across all
the applications
that we're developing.
We start seeing patterns.
We start seeing things that
are repeating themselves.
So we take those patterns, we
pull them down in the framework.
They either go from the apps in
the UIKit or they go in the apps
in the AppKit on OS X, or they
go down in the foundation,
or they wind up going
down into Core OS.
There are lots of
different places
where we start pulling
common code up out of apps
and make sure that they
wind up at the right level
of abstraction for
use everywhere else.
Right? So, a really good
technique in terms of separating
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Right? So, a really good
technique in terms of separating
out everything that you're doing
in your applications is
doing the same thing.
Figure out what's in common,
put it in the framework,
use the framework as
the unit of distribution
for pulling all that
stuff together.
Frameworks are API.
You heard Andy and Collin talk
a little bit about it yes --
yesterday -- earlier today.
Identifying the
responsibilities,
figuring out who should be
doing what within your classes.
Right? Some guy on the
internet wrote a thing
about being ruthless with
your objects and making sure
that there are these
clean, bright lines
between where those
responsibilities go.
It is a great technique
and it will make a lot
of your development easier
to take the same kinds
of framework approach to
developing your application
because now you get
really separable code.
You get easily testable
code, you get very,
very good abstraction layers.
Once you've decided you're
going to be offering frameworks
to your application or to
anyone else, you're going
to publish a framework as API
for your service, you know,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to publish a framework as API
for your service, you know,
providing all those
UI view controllers
that go along with it.
The keyword there is it's API.
The key thing is to make sure
that you get the API right.
We spend weeks arguing about
things on mailing lists at Apple
about how to develop the API,
what the architecture
should look like,
what things should be
named, all of that stuff.
There is some Cocoa guidelines
that we use, I'm going to run
through some of them here.
One of them is that it's
much better to be clear
than it is to be brief.
Right? So, a method like this
in Objective-C, insert:at:
is very brief, it's very terse.
What are you inserting?
What is the at that
you're inserting it to?
Don't do this.
It's not clear enough,
it's not specific enough,
it doesn't help you,
because, you know,
we all write this code,
what, once, twice.
We're going to read
it thousands of times.
I'm going to hand a
whole bunch of code off
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I'm going to hand a
whole bunch of code off
to some kid who's younger and
smarter than I am and he's going
to have to figure
out what I was doing.
Right? So, be clear,
express your intent.
insertObject:atIndex:, right?
What are you inserting?
Some object.
Where is it going?
At an index.
Right? Now you've got a
very specific guarantee.
You've got a readable
chunk of code.
This is the kind of thing that
we will literally spend a week
in email arguing
about to get right.
API design is like a
contact sport at our company.
So, if you're not really
throwing some elbows
in the mailing list with
your friends and neighbors
in doing API you're probably
just not having enough fun
with it.
So, do this.
Be really specific.
Don't abbreviate, right?
Don't' make your selectors
look like line noise.
All right -- setBkgndColor:
We have code completion,
it fills it out for you, you
can get a couple of keystrokes
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
it fills it out for you, you
can get a couple of keystrokes
into it and it'll
tell you, oh, look,
you know, setBackgroundColor:.
Right? It's long, it tells
you exactly what's going on.
Do that. Don't be ambiguous.
Right? There are a lot of words
that can be used as
a verb or a noun.
Here's one of the ones that I
actually proposes and managed
to get by and now
regret, displayName:.
Right? You see this in a
couple of places, I think it's
on NSFileManager these days.
We try not to do this anymore.
Is display a noun or a verb?
Am I telling this thing to
show the name to somebody
or is it a name intended
for display?
Right? What we do these days
in the Kit mostly is anything
that's intended for the display
to the user is a localized name.
Even if you're getting
as input from the user.
Because if you're getting
it as input from the user,
it's pretty much as localized
as it's going to get.
Right? It's coming from that.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Right? It's coming from that.
If you're showing it to the user
and it's in your strings files
and things like that,
it's the localized name,
it's coming out of the bundle
system to have the localization.
Right, so our intent
was to be able to say,
we want to show the users
something in their language.
It's for display.
And then everybody went, well,
why -- I'm calling displayName,
why isn't this drawing?
Oops, yeah, let's not do that.
So, be clear.
Also, don't take advantage
of some of the things
that Objective-C in
this case lets you do.
For instance, you could
declare sendAction like this.
And you don't necessarily have
to give names to the parameters.
So this produces a call site
that looks like sendAction::,
you know, sendAction: --
Selector,:object, :flag.
It's not specific enough, what's
the object intended to be?
What does that flag
actually mean?
Right? Fill the whole
selector out.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Right? Fill the whole
selector out.
sendAction, toObject,
forAllCells, YES or NO.
Right, that flag could
have been anything.
It could have been animated,
it could have been, you know,
stop transmitting the message
after you find a
hit or keep going.
Those kinds of things
have to be spelled out.
Again, you're going to write it
once, somebody's going to come
by later and curse your
name because they have
to read it 1000 times
after that.
Don't be that guy.
Do this. Swift, though,
brings us some really
interesting opportunities
to be clear, to be expressive
and still try maintain some
of the terseness and the
less typing and things
like that that we'd like to do.
And some of the things that
I'm doing to talk about as far
as Swift goes are a little
bit different because, for us,
we're still trying to
figure out what some
of the really good
API idioms mean
and we're really not
sure what it means
to have a language idiom
for a language that has had
about 200 native speakers
for less than a year.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
about 200 native speakers
for less than a year.
So, trying to figure out sort of
what some of this stuff is going
to wind up looking like is
a little bit of a challenge.
This is actually a great
opportunity for all of you
to be able to provide us
feedback with Swift in terms
of what the API looks like
and how your experiences
in using it both as a native
language and through the mix
and match bits with
Cocoa and Cocoa Touch,
and let us know exactly
what you'd like to see
and what makes it easy to use
Swift with all of these things,
versus what makes it feel
like there's a little
bit of friction there.
All right, so please make sure
that you're filing
those [inaudible]
on how you'd like it to work.
Let's take a quick look at a
Swift function, or I pulled this
out of the examples in the book
but I'm going to sort of work
through those and then we'll
extend it to something else.
So containsCharacter string:
String character: Character.
So a Swift function
has named parameters,
in this case these names
are the variable names
that you'll use inside
a function
to describe the variables.
Right, so this produces a call
site that looks like this,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Right, so this produces a call
site that looks like this,
containsCharacter"Buddy", "u".
Right, there's a string,
there's a character.
If you tack a little
hash mark in front of it,
this is not a social media
construct, you don't get to find
out what's trending in
your code [laughter],
although that might an
interesting question.
Right? How often do you use
hashtag character in your code?
This allows you to be able
to use a named parameter
at the call site and be
specific about what it is.
This produces a call site
that looks like this.
containsCharacterstring:
character:.
Java code -- you've got method
overloading, you can wind
up with a method that's one word
and it has 19 different
sets of parameters.
Some of them have, you know,
four arguments, six arguments,
12 arguments, nine arguments.
Some of them are the same,
some of them are different.
What arguments are they?
These named parameters are a
great way to be able to actually
to tell at the call sites which
ones you're using along the way.
It's a way to be expressive.
It's a way to be specific.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Right? One of the really nifty
things about Swift is the mix
and match feature, being able to
take code that already exists,
you've already got a
ton of Objective-C code.
In terms of practical terms and
shipping applications and things
like that, you'd be nuts
to throw that away and try
to do it all over
again in Swift.
It'd be fun if you had the
time but you probably don't,
you're trying to ship, right?
Mix and match lets you
do things like this.
This is the Popover
presentation API from UIKit.
It gets converted into something
that looks vaguely like this
by the mix and match
system for Swift.
Right? So, got a function,
presentPopoverFromtRect rect:
view: arrowDirections: animated:
That produces a call site
that looks like this.
Okay, again, not
specific enough,
not a really good description
of what's happening, right?
What's .Any there?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
What's .Any there?
I have no idea.
What does true mean?
I mean someRect and someView
are at least descriptive enough,
but that doesn't really help me
out if those aren't
my variable names.
Right? If we re-declare --
oh, and then we've got
presentFromBarButtonItem
and that just throws the
other wrench into it.
Right? Which is okay.
Well, now I got barButtonItem,
but arrowDirections
and animated are still there.
We can be more specific
about these things.
We can take advantage
of function overloading,
method overloading, and say,
let's call them both
presentPopover, what the heck?
All right, but now we've got
this really neat way to be able
to separate out the
other API from, you know,
Cocoa and Cocoa Touch, and
make these two different.
So what do we have here?
I've got fromRect rect.
Right? So fromRect is going to
wind up being the external name
that you use at the
call site, rect is going
to be the variable
inside the method,
the implementation
variable that you're using.
It's the parameter that --
it's the name that you use
for the parameter
that gets passed in.
inView view and then the
hash marks there again,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
inView view and then the
hash marks there again,
that makes it possible
to use the same name
as the external name,
as the internal name.
Those produce call sites
that look like this.
Right? So now I've got
presentPopover fromRect, inView,
ArrowDirections,
Animated, and presentPopover
from BarButtonItem,
arrowDirections, animated.
Great, I can tell the different
between them, that's awesome.
That's a lot of typing.
Co-completions nice but that's
still a bunch of typing.
Swift has some really
nice features
for default arguments
in your API.
So let's go back and take a look
at just the presentPopover
method.
Okay? If we re-declare
it like this,
what we've got is
UIPopoverArrowDirection is the
type, and .Any there
is the default value.
Right? Swift, when it
goes through the mix
and match bits takes the common
pre-fix off of the ENUM's
for the options and makes
them the shortened version
at the end, which is
one really nice thing
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
at the end, which is
one really nice thing
about when we went back through
and started normalizing
all the ENUM's again
and the common prefixes
allowed us to do this.
So, .Any as passed into a
popoverArrowDirection type is
that particular entry out
of the NS ENUM argument.
And Bool gets true there
is a way to say that YES,
if there's no argument
presented we want to be able
to have this thing
be true all the time.
So that turns these call
sites, which are nice,
they're descriptive,
into these call sites.
Right? One of the
patterns that you see
in foundation is you'll
see a really long method,
it'll have probably, I don't
know, anywhere between six,
and eight to 12 arguments.
And then you'll see versions
of that that are shorter.
Generally you'll see
that the end arguments
of an Objective-C [inaudible]
starting to get peeled off.
Those shorter versions
are calling
into the longer version
with default values.
Right? Those are all generally
one line implementations
that call back out to the big
guy with all the switches on it.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that call back out to the big
guy with all the switches on it.
Right? This is a way
that you can provide all
of those switches without having
to provide all those
methods all the time.
Put in the same default values.
Right? This provides you with
some really good opportunities
to be able to clean up your
code, be really clear in Swift,
and doing this in Swift,
talking to Objective-C API's,
you can start really
thinning down the API surface
so you don't have
tons and tons and tons
of methods to keep track of.
You've got one place to
do these implementations
and the default value's
just go in the right place.
There's really good
documentation online,
the "Coding Guidelines for
Cocoa" at developer.apple.com.
If you just go to
developer.apple.com, log in,
search for this document, it's
the same coding guidelines
that we have up on our
internal wiki for how
to get started with
developing API.
All right, so the rules that
we're trying to put down there,
they're the same things
that we use at Apple.
So another good reason to
go and look at these is all
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So another good reason to
go and look at these is all
of the Objective-C API
that you're going to write,
if you're not just going
to chuck it over and go
for Swift entirely, all of
that API will work well and fit
in with the idioms
that we already have
for our existing
Cocoa frameworks.
The other document you
probably want to read
that I'm sure you've
all downloaded already,
"The Swift Programming
Language."
I wouldn't be surprised if all
of you suddenly have
more experience with it
than I do by this point.
It's available on iBooks,
it's also available online
at developer.apple.com.
It's got all of the
information about Swift in it
and they do make some
API recommendations
in there as well.
Again, we're all sort of
just figuring this out.
So, if there's something
that you see that makes a lot
of sense, great, call that out,
tell us because we'll make sure
that we try to keep it.
If there's something you
see that you don't like
or that you'd like to make
suggestions of, send them in.
Take advantage of
bugreport.apple.com,
get the radars in on Swift
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
get the radars in on Swift
because this is really your
opportunity to have a lot
of influence on how all
of this stuff develops.
If you're publishing a
framework, whether it's
for your own applications, or
whether it's for other people
to use in their own
applications, you're going
to run into a problem.
This is a problem that we run
into every time we release a new
version of the operating system
and that's framework version.
Right? So your user has gone
and downloaded a
whole bunch of apps.
Darn them.
Well, I mean, good for you guys.
They should download
all of your apps.
All of those applications, let's
say, used the latest version
of this new framework that just
came out and its version 1.
So for a while at least
you're in good shape.
Everybody's got the
same code running,
if an application
sends information
to another one via an extension
or via the open URL call,
things like that, hey, all
of the objects work together.
No problem, right?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
No problem, right?
Awesome.
And then, you come out with a
new version of the framework
and some applications update to
it and some applications don't.
Or some of the applications just
got developed and then abandoned
and they'll never update.
Right? So some applications
on the user's device are going
to be Version 2, the blue ones,
some versions are
going to be Version 1.
And now suddenly I'm
trying to send an object
to that other application
from a Version 2 framework
to a Version 1 framework or
from a Version 1 framework
to a Version 2 framework.
Not everything's the same.
Those objects have
grown new capabilities,
the data that you're
passing back
and forth may have
new keys in it.
Right? One of the things that
you're going to have to do
if you are going
to be publishing a
framework is be very careful
about the versioning.
So, a number of things that
you can do about dealing
with framework versions.
First, you're going to have
to put a framework
version in there anyway.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to put a framework
version in there anyway.
The framework mechanism
when you create a new target
or when you create a new
project for a framework,
that goes in the
info [inaudible],
there's a short version string,
keep track of that, use that.
That's information that you
can use in order to figure
out what's happening,
what version
of the framework were
you linked against,
what version is the
data coming from?
You can send that data to
something else to say, hey,
I'm a Version 1 thing,
I'm a Version 2 thing.
The other thing you should
do probably is take advantage
of keyed archiving or keyed
archiving-like behavior.
Right? So if you are doing
key-value stuff as part
of your archiving or part of
your document format make sure
that your key-value pair
meanings are consistent.
Right? Don't suddenly
for a Version 1 key
that might be background color,
don't suddenly make
zero mean white instead
of black in Version 2.
Because then suddenly the
user's going to come along
and you're going to get sent
some data and they're going
to say, "Wow, this is
completely rendered wrong.
What happened?"
Right? So once you've committed
to what a key-value pair means
or what a relationship between
two key-value pairs mean inside
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
or what a relationship between
two key-value pairs mean inside
those archives that you're
sending back and forth
of different versions of
yourself, you really kind
of have to stick with it.
Right? We do this at Apple also.
There's this whole set of keys
that have sort of come along
to make sure that when we open
up archives from older versions
of the system that we can
continue to read that data.
You do get lots of interesting
opportunities at read
and write time to be
able to deal with that.
So, one is missing keys should
return 'nil' or the equivalent.
In the face of getting back
'nil' you probably want
to do something sane.
Right? If you're looking for
a key and it's not there,
maybe that's a great opportunity
for some default value
to get injected in at the
time that you're on archive.
Right? And that's
really the opportunity
to be able to do that.
Because the encoding and the
decoding are your chances
to get at that interface.
When you're writing that data
out, you're going to be writing
that data out for
consumption by somebody else.
When you're consuming that data
you kind of have to look at it
and go, well, I'm not
really sure where this comes
from so I should at
least be wary of it.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
from so I should at
least be wary of it.
Right? If you're encoding
things and you know
that there's a bug back there
that you need to fix back
in Version 1 and you can do it
by encoding something
different, you might do that.
If you're looking at situations
where you're pulling new keys
in, the old code isn't going
to go looking for those
new keys, so it's going
to ignore those things,
you're probably pretty safe.
The new stuff you want to
make sure you're not stomping
on old keys, you're
not interfering
with the relationships
between keys.
When you encode and decode
that's your interface.
I know I'm writing out
four something that's going
to pick us up and I don't know
what version it's going to be.
Or, I'm getting data
back in I have no idea
where it's coming from.
Right? Make sure your
keys are unique, right?
If somebody's sub-classing
your stuff
and they're sending stuff back
and forth but they're depending
on your framework, you won't
to actually stomp
on their name space.
Right? So the keys, even though
we're getting some name spacing
behavior in Swift, the
keys in these kinds
of things aren't
name spaced at all.
So make sure they're unique.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So make sure they're unique.
Use the prefixes, if you want
to be really defensive use your
bundle ID, prefix it there.
One of the ways you can also
mention this, I didn't put it
on the slide, one of the ways
you can also work with this is
by conforming to
NS secure coding.
Right? Secure coding
will make sure
that you're only getting the
objects out of the archive
that you're expecting
to be there.
Also, start looking for
feature availability.
Right? Classes respond to
selectors for a reason.
If you're in some code
and you're using somebody else's
framework you want to make sure
that your code is going
to keep functioning
if something gets updated.
Check to see if it
respondsToSelector.
Check NSClassFromString.
Do those things in order to make
sure that when you're working
with all of this
stuff you don't wind
up accidently pantsing the
user and killing their app
because of sending a message
to a string or a variable
that doesn't respond to that
message except in Version 2.
Okay, this is all
awesome, great.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
How does all this work?
Pretty well actually.
If you follow me on
the social networks
of the day you probably
know that I had a dog.
This is my dog.
This is Buddy.
He's got a 60-pound bark
and a 30-pound body.
He's about this tall.
And he's cute, and I post
a lot of pictures of my dog
because I can't really post
anything about work [laughter].
So, a lot of people
like to say, "Hey,
Chris, your dog is awesome."
You know, "Can I
borrow your dog?"
"I'd love to meet your dog."
"Your dog would be a
great part of my life."
I'm not giving away
my dog, sorry.
You can't have him.
But I am pleased to talk a
little bit about a prototype
that I've been working on.
It's called Budstagram
and it's a way
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It's called Budstagram
and it's a way
that you can take your pictures
and put my dog into
them [laughter].
So --
[ Applause ]
What we'll do is I'm
going to take a quick tour
through Xcode both on -- for
what I've done so far with this
and just to walk through the
initial setup of an extension
and an application
and a framework.
But you're going to be able
to do this kind of thing.
The idea is -- but this is
a terrible picture isn't it?
This is a beach in Corsica.
I went on a biking
trip a while back.
Our ride wound up here.
It was absolutely gorgeous.
We were going to try and
stay a couple of days
but they weren't
going to let us do it.
But this is a great picture
but I really missed Buddy.
I'd been gone about
10 days by this point
and it's a great
picture but now,
now it's an even better
picture having been photo bombed
by my dog.
So --
[ Applause ]
We're going to take a
look at three things.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We're going to take a
look at three things.
Budstagram, the application,
is sort of the icon
that the user sees,
it's the place
that they're going to start.
Right? It's going to allow
posting and editing of photos,
and as long as there's a picture
of Buddy in there it'll post.
We'd like to allow some
comments on existing photos,
sort of a feed kind
of mechanism.
You may have seen apps like this
before somewhere, I'm not sure.
And we're going to have it
share a posting view controller
with the extension.
Right? We want to
share that code
so it winds up in both places.
The extension is going to allow
posting and editing of photos
within other apps,
but it's not going
to allow comments
on existing photos.
We're not going to put
the feed in the extension.
That's the business
of the application.
And that's actually one
thing about this architecture
in the frameworks and stuff
like that that you want
to be really careful about.
Right? You don't want to wind
up with your entire application
suddenly living in the framework
because not all of your
applications can be appropriate
for the extension or not all
of your applications can be
appropriate for publishing
as a third party framework.
There's the things that
are common to your service
and there are things that
make your app unique.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and there are things that
make your app unique.
Right? So you want to try and
keep those two things separate.
And it's going to share
the posting view controller
with the application obviously.
The framework is going
to contain the shared
posting view controller
and the shared networking code
for posting and things
like that.
And the model code.
Right? All the stuff that
makes up the common things
that the extension needs
to know to be able to deal
with the objects that the
applications also deal with.
So we have the Budstagram
application here.
And if I tap on it,
there's Bud in Corsica,
this is the feed here,
and if we were going
to post something
here, this is a picture
of my parents' backyard
in Connecticut looking very
New England in the fall
and it's very pretty and
I can't bring Bud there
because he doesn't like to fly.
We've got this app,
we've got the extension,
they work together.
You're going to be able to
take data from the extension,
throw it into the application
and the code that you're going
to share between the
two, you're only going
to use in the framework.
So you don't have to be
jocking targets, you don't have
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So you don't have to be
jocking targets, you don't have
to be shuffling stuff
around, things like that.
So, let's take a look at Xcode.
So let's take a look
at a new Xcode project.
I'm going to start from scratch
here mostly to give you sort
of an orientation before I start
jumping around in Budstagram
to show you what I did.
So, what we've got here
is the start of, say,
a tabbed application or
a page-based application.
Let's go with the single
view application here just
for the basics.
We'll call this framework
example
because I'm not particularly
creative.
And we've got a language
choice here, I'm going to stick
with Objective-C for a
minute and we'll just --
oh, I am not an organization
unto myself, so we'll call
that Apple, Incorporated.
Desktop will create this.
So we've got here now
is just an application.
Right? And there's some
different things here,
there's some build settings, and
build phases, and all this stuff
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
there's some build settings, and
build phases, and all this stuff
that you're familiar with.
You'll see the link binary
with libraries bit here.
And if you were just going
to have an application
this would be fine,
you'd work with this and
you'd put all of your code in
and your tabs and you'd work
with your targets and things
like that, but we're
actually going to go ahead
and create a new target here.
And we'll create a new
framework and library target,
a Cocoa Touch Framework.
We'll call this --
let's call it Budstakit.
And you'll see two
new things here.
It says here the project is
the framework example here,
and embed in application
framework example.
So, we're creating this new
framework and Xcode's also going
to set it up so that when
a framework builds and then
when you go and archive
it all that stuff winds
up in the right place.
So we can do Finish here.
And now we've got the link
binary with libraries there.
Budstakit framework is required.
And let's go ahead and actually
create the extension here too
while we're at it.
So here's an application
extension.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So here's an application
extension.
We'll use a share extension.
Okay, and this'll be the
BudstaPosting extension.
Okay. So now we've got
BudstaKit, BudstaPosting.
The BudstaPosting bits have the
share view controller in here,
and a storyboard for that.
Let's see.
We pause for station
identification.
So there's nothing in
BudstaKit right now.
There's nothing in the --
there's this sort of empty
share view controller in here.
And we've got this
ViewController
for the framework example
for the application.
One of the things that I
mentioned before was I wanted
to be able to have the share
view controller be part
of the framework.
Right? Because I
want to use that both
in Budstagram itself
and in the extension.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in Budstagram itself
and in the extension.
BudstaPosting is the extension.
All of this stuff,
if we go look,
let's take a quick look
at the Finder here.
Actually, if we go look in the
framework example folder, right,
Xcodes are organizing all
the code in directories here,
but the directory -- obviously
directory hierarchy is not
necessarily reflected in how
Xcode shows that in its files
and groups thing here.
But we're going to just move
these guys up into here.
So I'm just making the
share view controller part
of the BudstaKit group,
it's not actually moved
out of the directory that
it's in behind the scenes.
But, you know, if you feel like
you want to do that go ahead
and move it with your
version controller system
over into the right spot.
Here in the BudstaKit header
I'm going to go ahead and,
it says here, in this header,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
it says here, in this header,
you should import all
the public headers
of your framework
using statements
like import BudstaKit
PublicHeader.
So let's do import
BudstaKit ShareViewController.
Hello code completion.
And then we need to make
sure that we change these
to be part of the right targets.
Over here on the right-hand side
ShareViewController.m is part
of the BudstaPosting target, we
actually want to make it part
of the BudstaKit target.
And the thing that
you're going to do
with the frameworks is different
than what you've probably
been doing before,
is within a framework
there are some designations
for what a header means.
So, you're actually going
to wind up adding the header
to be part of the
target of the framework,
which isn't necessarily
something you've really
done before.
Right? Usually you use
the headers, the .m's,
or the .s's are part
of the framework --
or the target, the headers are
just loose, they never wind
up getting distributed.
So since this is a framework
and you're going to hand it
out to people, you're
going to be handing it
out to other developers on
your team, you're going to want
to tell us whether this
thing is a project header,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to tell us whether this
thing is a project header,
a private header,
or a public header.
Right? So the project header
means that only things
within the project can see it.
The private header goes --
anything that's marked
as a private header goes
into a private header's
directory inside the framework
when that framework gets built.
Right? The public portion of it
is obviously the public headers,
it's the headers that are just
in the headers directory there.
I'm going to make
this a public header.
You might use private
headers because just
like us you've got SPI that
you don't want people to use
but it's really handy to be
able to use it internally
or across two projects,
and maybe that SPI is something
you want to publish later
but it's not quite bulletproof
enough to be able to be API now.
You'll put that in
a private header.
If you publish your
framework, don't forget
to remove the private headers
directory from the framework,
because then everybody
can grope all over it.
So, and then down here in the
BudstaPosting thing, well,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, and then down here in the
BudstaPosting thing, well,
we're going to have
to edit the target.
So, let's go back up to
this framework example here,
and if I look at BudstaKit,
BudstaKit is linking
with no libraries.
The framework example is
linking with BudstaKit.
I need to make BudstaPosting
link with BudstaKit,
so what I can do is just drag
this guy here -- no, okay.
We'll pick a plus, we'll choose
BudstaKit from the project,
and now all of those
things will link together.
So now ShareViewController
is now part of the framework.
Since BudstaPosting the
extension links the framework
and the app links the
framework they can both
use ShareViewController.
Let's go ahead and build
that for the simulator.
And then let's take
a quick look here
at what actually got produced.
So, if we go look at -- I moved
my build directory up here
on the desktop so it
would be easier to find.
And if we go look in products
here, we've got a couple
of different products based
on our target configuration
and our target.
Here's sort of everything
spread out.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Here's sort of everything
spread out.
Right? So here's
BudstaKit.framework.
Here's the app.
Here's my Budstagram application
that I was building earlier.
Let's look at framework example.
Inside that is now a couple
of different directories.
This frameworks directory has
the BudstaKit framework inside
of it and let's get
all old school
and go look in the
terminal here.
The old tricks are the best.
And the BudstaKit framework
has inside it this headers
directory, and a module.
Modules are generated for use
with Swift and for the module
at import directive
for Objective-C.
There's some nifty things
that go along with that.
But those are things
that you should read
about in the Xcode
documentation.
So, the framework is built
into the application,
that's actually one thing to
be aware of is you're going
to have multiple copies of the
frameworks on disk at a time.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to have multiple copies of the
frameworks on disk at a time.
We're not sharing frameworks
across sandbox boundaries.
Right? Things inside the same
sandbox could access the same
framework, but we're not
letting you use the framework
out of some other
application installed even
if it's one of your own.
All right, so all of these
things get imbedded directly
in the application.
So your apps are still
completely stand-alone items.
So, let's go back to this
and take a quick look here.
Because the extension is its own
little application some things
you need to be aware
of are things
like NSBundleMainBundle
isn't going
to work exactly the
way you think.
So you're going to have
to go look for assets
by bundle identifier
using the bundle API.
No that isn't a bug
I ran into myself
that I introduced,
why do you ask?
So, and the ShareViewController
has the standard extension stuff
here for things like
isContentValid, didSelect.
These are peculiar to
the ShareViewController
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
These are peculiar to
the ShareViewController
but there are other API on
all the other extensions
that are specific
to those as well.
So let's actually take a look
and see how I sort of divided
up BudstaKit and Budstagram.
So, there are a lot of
really similar things here
to the way everything
got broken down.
Not really tall enough to
point up there but you'll see
that we've got the
feed view controller
and a posting view controller,
which are the two things
that you saw in the tab
view in the demo earlier,
and then the main storyboard
contains those two things.
And then there's a bunch
of model code that's
down in BudstaKit, and
BudstaKit has all of the stuff
like the public umbrella header.
Right? So just like you just
type importUIKit.h you should
setting your umbrella header
in your framework to
do the same thing.
Put all your public API in there
so you'll import BudstaKit.h.
Or at list I hope
you will someday.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Or at list I hope
you will someday.
And then also, all of the model
codes down there, so the thing
that manages a post is there.
And the post view
that displays a post
with the dog resizable there
and the ShareViewController
and things like that.
And then down here in
the Budstagram poster,
again, it's the same thing.
It's just the main
interface storyboard,
the empty storyboard, which
says when you call up one
of these things this is the
storyboard to launch into.
Right now in the
extension that's it, right?
There's other logic that
lives in the framework
that does all the other
work, the sending of the data
and the manipulation of the
photo and things like that.
So making sure that you're
pulling all of these things
down into the right place
is very, very, important.
Most of those 13 years I've
spent doing frameworks I've
worked for a lot of really
talented frameworks developers,
a lot of this stuff is
tremendously hard-won lessons
from shipping software.
Right? Being clear,
being explicit,
having the right amount of
API, having expressive API,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
having the right amount of
API, having expressive API,
distributing things in the
right way, being careful
of your versions, making
sure that you're working
with data safely, getting
the right abstractions.
Right? All of those things make
our lives much, much easier.
Taking that frameworks approach
even to the applications.
If you look at most of our
applications, on the books,
you know, Maps has MapKit.
Right? On iOS 8,
Health has HealthKit.
Right? All of the things that
make the app function and useful
to the user live in the app.
All of the heavy lifting,
all of the shared code,
all of the interfaces that
allow the things to work
with your app go in
those frameworks.
Right? It's a great
separation for us,
it's what helps us
ship software,
it can be a great
separation for you as well.
There are some related
sessions that happened earlier
in the week and there's
one tomorrow.
Creating Extensions
for iOS and OS X,
Parts 1 and 2 were earlier.
Go watch those for breaking
up things into the extensions
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Go watch those for breaking
up things into the extensions
and into the mechanics
of setting extensions up.
Right? And then apply some of
the stuff that you got here
or some of the stuff
that you got from Andy
and Collin's talk previously,
in the previous hour,
to all of that.
And then sharing code
between iOS and OS X.
A lot of you work on
both platforms and a lot
of the model code and things
like that is actually shareable.
And much of the extensions
API was developed specifically
with an eye to being
able to write
as much cross-platform code
as possible with as little
of the #ifs for target
OS as we could manage.
Right? So you will be
able to do a fair amount
of code sharing even for
extensions in iOS and OS X.
That's right here tomorrow
morning at 9:00 am.
Bright and early.
Okay? So, please have an
absolutely fantastic remainder
of the week.
Enjoy the bash.
Thank you, very much.
[ Applause ]