Transcript
[ Music ]
[ Applause ]
>> Hi everyone.
Welcome to session 221, TextKit
Best Practices.
I'm Donna Tom.
And I'm the TextKit engineer.
And my colleague Emily Van Haren
from authoring tools will be
joining me today.
And we're both really excited to
share with you some best
practices for working with
TextKit.
So let's get started.
First, we're going to review
some key concepts for working
with TextKit.
Then, we'll dive into some
examples to illustrate how to
apply the key concepts in your
app.
And finally, we'll wrap up with
some best practices in the areas
of correctness, performance, and
security.
So let's start with the key
concepts.
Now to make sure we're on the
same page, we're going to start
at the very beginning.
What is TextKit?
And your first instinct might be
to open a shiny new playground
in Xcode and type import
TextKit, except if you've ever
actually tried this, you found
that it doesn't work.
And that's because TextKit is a
little different than other
frameworks you might have used.
You don't have to import
anything special to use it.
The text controls in UIKit and
AppKit are built on top of
TextKit.
And so if you've ever used a
label, the text field, or a text
view, you've actually used
TextKit.
And TextKit pulls together
powerful underlying technologies
such as Core Text, Core Graphics
and Foundation to make it simple
and seamless for your apps to
show text.
And every time you use one of
these built-in controls, you're
using the power of TextKit to
show or edit text in a fully,
internationalized, localizable
manner without having to
directly use these underlying
technologies or understand the
intricacies of complex scripts.
And there are a lot of things
you get for free too like all of
these display features that you
see here.
And for editing, you'll also get
access to all the tech services
that are supported by the OS
like accessibility, spell
checking and more.
And you can take advantage of
all of these great features
without having to write a single
line of code and that's pretty
awesome.
And so with all of this
functionality at your
fingertips, how do you decide
which control to use?
So let's talk about that,
choosing the right control for
your situation.
And the options are going to be
a little bit different depending
on whether you're using UIKit or
AppKit.
So let's review them separately.
All right.
Let's start with UIKit.
And first you're going to
consider whether you need text
input.
And if you don't need text
input, then consider whether you
need selection or scrolling.
And if you don't need these,
then you should use UILabel.
UILabels are intended for small
amounts of text like a few words
or a few lines.
And so if you have more text
than that or if you need these
selection or scrolling
capabilities then you should use
a UITextView with editing
disabled.
Now going back to the top.
If you do need text input, then
consider whether you need secure
text entry.
And this would be like a
password field where the text is
obscured and copying is
disabled.
And so if you need that, then
use UITextField because this is
the only control that supports
secure text entry.
Otherwise, think about how much
text you expect to be entered.
And if you want something that's
like a form field input that
only needs a line, then use
UITextField.
And UITextField only supports
one line of text entry.
Otherwise, if you need more than
that, you can use UITextView.
And so now here's that same
decision process for AppKit.
And it's similar to the UIKit
process but there's a few small
differences.
So, again, you're going to start
by considering whether you need
text input.
And AppKit doesn't have a label
control.
So if you need to display text,
use an NSTextField and you can
disable both editing and
selection to get that label
behavior.
Now going back to the top here.
If you do need text input, again
ask if you need secure text
entry.
And if so, you can use
NSSecureTextField.
Otherwise, we're going to ask
our favorite question, how much
text do you expect?
So NSTextView is optimized for
performance with large amounts
of text.
And so if you're expecting a lot
of text, you should use
NSTextView otherwise you can use
NSTextField.
Now, unlike its UIKit
counterpart, NSTextField does
support multiple lines of text,
but it's still optimized for
shorter strings and so you
should still use NSTextView if
you have a lot of text.
Now those of you who have been
around the block a few times
with TextKit might notice that
the flow charts are missing an
option and that's string
drawing.
And you use string drawing by
directly calling draw in point
or draw in rect methods under
NSString or your
NSAttributedString.
And many of you may be using
this for the performance benefit
to avoid the overhead of view
objects at the kit level.
And so if you're going to go
this route, please keep the
following tips in mind.
You want to use it for small
amounts of static text.
And you want to limit how
frequently you call the draw
methods.
Now if you're calling the string
drawing methods a lot, you might
actually get better performance
out of a label or a text field
because these controls provide
better caching, especially if
you're using auto layout.
And if you're drawing an
attributed string with a lot of
custom attributes, this could
also be slowing down your string
drawing because the text system
needs to validate all of the
attributes before rendering and
so for best performance, you
should strip out extra
attributes before drawing and
only pass in the ones that are
needed to determine the visual
appearance like font or like
color.
And finally, remember that by
using string drawing, you'll
miss out on all of this free
functionality that's offered by
the text controls, so you should
use the text controls whenever
possible.
So now you know what you can do
with TextKit just by using the
built-in controls.
But if you want to go beyond
what these controls provide,
you'll need to find the right
customization point within the
text stack.
And like much of Cocoa, TextKit
is based on the model view
controller design pattern.
And the text system can be
divided into three phases that
correspond it directly to NBC
and that's storage, display, and
layout.
And so now let's take a closer
look at the TextKit objects that
make up each of these phases.
And we'll start with the storage
which corresponds to the model.
Now NSTextStorage holds your
string data and your attributes.
It's a subclass of mutable
attributed string and so you can
work with it in the same way
that you already know how to
work with attributed strings.
And my colleague Emily will show
you some really powerful ways to
customize the text storage a
little bit later so stay tuned
for that.
Now NSTextContainer models the
geometry of the area where your
text will be laid out.
And by default, it's a rectangle
but you can customize the flow
or the shape of the text layout
as shown here.
And for more detailed
information on working with the
storage objects, check out these
great past WWDC sessions and
documentation.
And they'll be available from
the more information link at the
end of the session.
And next up is the display phase
and that corresponds to the
view.
And we've already talked about
the display phase quite a bit
when we talked about choosing
the right control.
And so for additional
information, you can again check
out these documentation
resources.
And they'll also be accessible
from that more information link
at the end of the session.
And finally, we have the layout
phase which corresponds to the
controller.
And NSLayoutManager is the only
component in this phase.
And let me tell you it is a
beast.
And I mean that in a good way
because it's so awesome at what
it does.
So it's the brains of the whole
operation.
It coordinates changes between
all of the phases, and it
controls the layout process
itself.
So here's a quick overview of
how that layout process works.
So text layout happens after the
system fixes attributes in the
text storage to remove
inconsistencies like making sure
that all the characters in the
string are covered by fonts that
support displaying those
characters.
And so in this example, the
Times New Roman font is
specified for the entire string,
but this font doesn't support
displaying Japanese kanji or
emoji.
And so after attribute fixing,
your text storage will look
something like this with an
appropriate Japanese font
assigned to the Japanese
characters and the emoji font
assigned to the emoji character.
All right.
So once the attributes are
fixed, the layout process can
begin.
And we can think of layout in
two steps: glyph generation
followed by glyph layout.
And once they're laid out,
they're ready for display.
But wait a minute.
What's a glyph?
Let's back up and review that.
A glyph is a visual
representation of one or more
characters.
And as you can see here, the
mapping between characters and
glyphs is not always one-to-one.
So here this string ffi has
three characters, but it could
be represented by a single glyph
for the ligature.
And you can go in the other
direction too.
Here we have n [inaudible] which
is a single character that can
be represented by multiple
glyphs: one for the n and one
for the tilde.
And so going back to our diagram
here, we have NSLayoutManager
performing glyph generation and
glyph layout.
And glyph generation is where
the layout manager takes the
characters and figures out what
glyphs need to be drawn.
And glyph layout is where the
layout manager positions those
glyphs for display in your view.
And there's a lot more to learn
about the layout manager from
these past WWDC sessions and
documentation.
And you can access them from,
you guessed it, the more
information link at the end of
the session.
Okay. So now you understand the
phases of the text system.
And you know the TextKit
components that make up each
phase.
So now let's explore choosing
the right configuration of these
components to create different
effects.
So this is your standard
configuration.
And when you drag and drop a
text view from Interface
Builder, you'll automatically
get one of each component as
shown here.
And most the time this is all
you're going to need.
If you want a multiple page or a
multiple column layout, you can
use pairs of text containers and
text views, one pair for each
page or column.
And you can hook all of these up
to the same layout manager in
the same text storage so that
they share the layout
information in the backing
store.
And if you want different
layouts in each of you, you can
do that too, just use multiple
layout managers.
And, again, since the text
shares the same backing store,
updating that text will update
all of the views.
Now we didn't go into too much
detail about these
configurations because there's a
great past session that's
already done that so check out
WWDC 2010 session Advanced Cocoa
Text Tips and Tricks.
And this will be accessible from
that more information link at
the end of the session.
All right.
So we've looked at the built-in
text controls.
We've looked at the components
in TextKit.
And we've look at how to
configure those components to
achieve different effects.
And there's a lot that you can
do with that knowledge already,
but if you need even more,
you'll need to extend and
customize parts of TextKit
yourself.
And so now we'll talk a little
bit about choosing the right
approach for doing that.
And choosing the right approach
is like building up your text
toolbox.
It's like going to the store
because you need a hammer.
And then when you get there, you
encounter this giant wall of
hammers to choose from.
And you want to pick your hammer
that can do the job and ideally
the least expensive one that
will do what you need.
And so these are the hammers
that are available to us.
Delegation is like your standard
hammer with the claw on the end,
and it's used to perform
multiple tasks.
So the delegates have a lot of
different customization hooks
and most of the time they'll get
the job done for you.
Notifications is like a
ball-peen hammer.
And this has a ball on the end
instead of a claw so it's more
specialized and it's better
suited for certain tasks, but
it's not as versatile as the
standard hammer of delegation.
And finally, subclassing is your
sledgehammer.
The sledgehammer is very
powerful, and you can use it for
just about anything that you
would need a hammer for but it's
probably overkill for a lot of
things.
And with that, I'd like to
invite Emily up to show us how
to use these different kinds of
hammers.
Emily.
[ Applause ]
>> Thank you, Donna.
So, as developers, we have a
collection of controls to choose
from, various configurations,
and a wide range of
customization options to achieve
what we need.
So our tool chest is stocked
full, but how do we know what
tools to choose?
So we're going to take a look at
some examples of apps that
harness the power of TextKit.
And we don't have to look very
far because almost every app
that we use displays or edits
text.
We're going to start by looking
at two apps that we're all
familiar with and then go
through the steps of building
our own.
So the first app we're going to
look at is Apple News on iOS,
which is a beautiful app that
displays text in a personalized
and curated articles.
So here's an example of an
article that is featured in the
spotlight tab.
Now the top of the app shows
some details about this article.
Now how could we use TextKit to
re-create this look and feel?
So let's consider the flow chart
that Donna showed us earlier to
pick the control that's best
suited for this example.
So we have a handful of text
controls to choose from, but
since we want to display small
amounts of text, each on a
single line, we'll use a label.
Now we can see that there is a
ton of customization options in
the inspector panel.
So we're going to go ahead and
change the text to spotlight.
We're going to change the font
to use the body style.
And we're going to enable
dynamic type, which allows those
with accessibility settings
enabled to see text in a font
size and style that is
appropriate for their needs.
Now it's great that we can
customize this label in
Interface Builder, but we can
also see all these properties in
Swift.
So we can set the text and the
formatting properties
dynamically at runtime.
Now back in Interface Builder,
we'll go ahead and add two more
labels.
Now everything fits pretty well,
but we have one more thing we
need to do here.
So looking back at Apple News,
we can see that the text on the
right is actually displayed with
two different colors.
Part of it's black and part of
it's white.
Now we could achieve this with
two separate labels, but if we
wanted to use just one label, we
wouldn't be able to do this in
Interface Builder.
So how could we do this?
Well, we can take advantage of
the power and flexibility of
attributed strings.
Now an attributed string is a
run of characters that can have
attributes applied to ranges of
characters.
Now some attributes you get for
free like the default font and
text color, but we can override
these attributes with our own
values.
In this case, we're going to set
part of our string's text color
to white.
Now to see attributed string in
action, we'll use the add
attribute method on
NSMutableAttributedString to set
the text color to white just for
the range that we want.
And this time we'll set the
attributed text property on our
label.
At runtime, this looks pretty
spiffy.
Now UILabels were a great choice
for this sort of text.
Now if we look at the bottom of
the screen, we'll see a
headline.
Now this is also text, but it's
a little bit bigger and it spans
multiple lines.
Another thing that makes this
text different is that it's
selectable.
So which control should we use
this time?
Now both text field and text
view support selection but text
field is meant for usually just
one line.
So in this case, since our
headline can span multiple
lines, we're going to use a text
view.
Now when we put a text view onto
our storyboard, we can see that
we get a lot of lorem ipsum text
by default.
So we're going to go ahead and
change the text in the inspector
panel.
We're also going to change the
font to look a little bit more
like Apple News.
And we want to disable the
editing feature because the
headline isn't really editable.
Now UITextView scroll by default
because they are a subclass of
UIScrollView.
But if we want our text view to
play well with auto layout, we
want to disable scrolling.
So this will allow the bounds of
our text view to resize to fit
the text.
Last but not least, this white
background really needs to go,
so we're going to set it to
transparent.
Now Interface Builder made it
really easy to customize this
text view but just like our
labels before, we can set all
this in code.
So here in Swift, we can set the
text and the formatting
properties dynamically at
runtime.
So we looked at Apple News to
pick the right control, but now
we're going to look at a
different app that we're all
familiar with to choose the
right configuration and that's
TextEdit.
Now TextEdit is an app on macOS
that handles display and editing
of rich text content.
Now what most people don't know
is that TextEdit is actually a
really thin wrapper around
NSTextView.
So I want to take a moment to
marvel at just how much we get
for free with TextKit.
So this is the inspector bar,
and we get this for free just by
checking a checkbox in Interface
Builder.
And right below it is a ruler
view which we also get for free
just by enabling it.
And everything below that is
just a text view.
Actually, it's a text view, text
container, layout manager, and
text storage.
Now this is the standard
configuration for both
NSTextView and UITextView, but
the similarities mostly stop
there.
So, for example, tables are only
supported in NSTextView.
And marveling again at the power
that we get for free, TextKit
provides a table editor that
does all the heavy lifting for
us.
Now when we use TextEdit, we're
often editing large amounts of
text.
Sometimes we paste in a lot of
lorem ipsum to see that we also
get a spell-checker for free.
But really what we want to see
is that when we use the format
menu to choose wrap to page, we
end up with it looking a little
bit more like a page.
We can see that the text
container has been resized to
match the dimensions of a piece
of paper.
Now if we scroll down, we can
see that the text jumps from the
first page to the second.
Now the standard configuration
doesn't really support layout
like this.
Sure enough, this layout uses
two text views and text
containers.
Now they're still managed by the
same layout manager and text
storage, which allows the text
to freely jump from one page to
the next.
Now if you'd like to see more
about how TextEdit works, you
can actually find its source in
the guides and sample codes
library.
So we've picked the right
controls, we've picked the right
configuration, but sometimes we
actually need to hammer on these
to achieve what we want.
But how do we decide which
hammer to use?
So we're going to try and pick
the right hammer for the job
when we go through the steps of
building a journal app together.
We'll start by putting today's
date on to the window.
Now we don't have UILabels in
AppKit, but we can make a text
field behave like a label.
All we need to do is disable
editing.
Now for the journal entry part
of the window, we're going to
use a text view.
So in the inspector, we can make
sure that the text view is
editable and selectable and
supports rich text and undo.
We're going to add a couple of
text fields to the bottom of the
window as well so that we can
show how many words have been
written.
Now when we run our app, we want
the word count at the bottom to
change, so let's find the right
hammer for this job.
Now we can either conform to a
delegate, handle a notification
or subclass.
But in this case, we're going to
use a small hammer.
And we're going to listen for a
notification from text storage.
Now we can get the number of
words from the text storage.
And when we hear the
notification, we can update the
string value property of our
text field.
And when we start typing, we can
see the word count change.
Now if we want to emphasize part
of our text, we can use keyboard
shortcuts or the menu to apply
formatting like bold.
But it would be great if we
could support modern text
formatting like Markdown, which
uses control characters to
specify formatting.
So if we start inserting
asterisks before and after, we
want it to be bold.
But which hammer should we use
for this?
Well, we want to know when a
change happens, and we want to
know where a change happens.
But notifications don't really
give us much information about
this change.
So we're going to use a bigger
hammer and implement the text
storage delegate, specifically
the didProcessEditing method.
Now we can make a new bold font
from our existing one.
And we can add that font
directly to our text storage for
the range that we want to be
bold.
And now when we insert that last
asterisk, we can make it bold.
Now we're feeling pretty good
about this whole Markdown thing
so what if we try inserting a
code snippet?
Now in Markdown it looks like
this.
And if we add this last
back-tick, we want it to look
like a code block.
It should have a background and
a header that says Swift Code.
Now this is actually a complex
task, so we're going to need two
sledgehammers.
And the first is a subclass
NSTextStorage.
Now when we subclass
NSTextStorage, we need to
implement four required methods.
And we can do this by operating
on a private instance of a
mutable string.
Now let's pay attention to the
replaceCharacters method.
Now we can add an NSTextBlock to
our paragraph style.
And then we can add that
paragraph style to our text
storage over the range of that
code block.
Now NSTextBlock by itself
doesn't do any custom drawing by
itself.
So we'll need to subclass that
too.
Our NSTextBlock subclass needs
to have some padding with some
extra padding on the top and a
light gray background.
We'll override drawBackground
and use string drawing to draw
the header Swift Code.
Now this is actually all we need
to do to make a text block look
like a code snippet.
Now back in our custom text
storage, we can create an
instance of our new code block
instead of using a plain text
block.
Now, last but not least, we need
to tell our text view to use one
of our custom text storages, so
we'll replace the text storage
on the layout manager.
Now this is turning into a real
WYSIWYG Markdown editor.
Now a popular feature of most
Markdown editor's is a
side-by-side view with an
editing version on the left and
a rendering on the right.
Now we can do this with two text
views side-by-side.
We'll disable editing for the
one on the right.
And now we have two text views
but we want them to display the
same content but look a little
different on the right.
So we want a configuration like
this where we have one text
storage but two of everything
else.
To do this, we will replace the
text storage on the right with
that from the left.
Now let's see what this looks
like.
Now this is actually really
cool.
If we add any characters to the
left, they'll show up
immediately on the right-hand
side.
Now usually the right-hand side
doesn't really show the Markdown
characters but since this is a
shared text storage, it means we
have to hide the characters
during the layout process.
Now since we need to do it this
way, we really only have one
option and that's to implement
the shouldGenerateGlyphs method
on the NSLayoutManager delegate.
This will allow us to intervene
in the glyph generation process.
So we can take the glyphs that
are about to be laid out and if
they represent a Markdown
control character, we can apply
the null property to that glyph.
Now this will eliminate the
glyph altogether during the
layout process without changing
the underlying text storage.
Then, we will use the new glyphs
and tell the layout manager that
we want to present these glyphs
with our new properties.
Now this is actually really
cool.
So the left-hand side shows an
editable version with all the
Markdown characters included.
And the right-hand side shows no
Markdown characters all, all
using the same text storage.
Now building a side-by-side
Markdown editor is not something
all of us do every day, but it
was really good to see how
customizable TextKit is with
real world examples.
If you'd like to learn more
about how to use and customize
TextKit, check out our amazing
programming guides.
And with that, I will hand it
back to Donna.
[ Applause ]
>> Thanks, Emily.
Those are some really cool
examples.
And I really hope you'll be able
to take some of the techniques
that she showed off and use them
in your own apps.
But now let's shift gears a bit
and talk about some best
practices for working with text.
So on the topic of correctness,
if your text doesn't render the
way you expect, it could be
related to incomplete or
incorrect attributes on your
attributed string.
And so let's take a look at an
example to see this in practice.
Let's say we have a UITextView
with some attributed text that
says don't hate.
And it says this in the font
Comic Sans 24 point.
And we want to programmatically
apply a bold typeface to the
word don't because if there's
any font more universally hated
than Comic Sans, it's Comic Sans
bold.
And so at first blush, it might
seem reasonable to write code
like this.
Now here we have our original
font.
And we're going to use a font
descriptor to create a bold
version of this original font.
Then, we're going to initialize
our mutable attributed string
using the original text.
We're going to apply our new
font or new bold font to the
word don't and that's going to
be the first five characters.
And then we're going to set the
attributed text property of our
UITextView to use this new
attributed string except when we
do that we'll see that our new
bold font applied to the word
don't just as we expected but
the rest of the string somehow
lost the original font.
And now those of you who despise
Comic Sans might be happy about
that, but the result is wrong
and so that warrants a sad face.
So why did this happen?
And to answer that, let's take a
closer look at how we're
initializing our attributed
string.
So notice that we're using a
plain text string to initialize
it, and we're using the
initializer with no attribute
information.
And when you create a new
attributed string and you don't
provide any attribute
information, that new attributed
string, we use the default
attributes.
And the default font is
Helvetica 12 point.
And so to recap what happened,
we started with this original
attributed string with the font
Comic Sans 24 applied to the
entire range.
And then we created this new
attributed string, and it got
initialized with the default
attributes.
And we applied our bold font to
the word don't on this new
string, and we ended up with
this incorrect result here where
the word don't is in Comic Sans
bold 24, and the rest of the
string is in the default font of
Helvetica 12.
And so there are two different
ways that we could do this
correctly and one way is to
avoid mixing the plain and
attributed text altogether.
So by initializing our new
attributed string using the
original one, we're going to
keep those original attributes.
And then we can apply our new
attributes without getting this
reset effect with the default
ones.
But it's not always feasible to
just avoid mixing plain and
attributed text.
So if you've got to mix it up,
you can explicitly supply the
attributes when creating that
new attributed string from the
plain text string.
And if we make sure to apply the
same attributes from the
original text, we'll get the
correct result.
But you should be aware that
this reset effect can happen
with any attributes that have
default values and not just
fonts.
And as you can see, there are a
lot of attributes with default
values.
So I'd like to call out the
paragraph style here in
particular as being a sneaky
reset point.
And to see why, we'll revisit
our earlier example.
But instead of changing the
font, we're going to change the
paragraph style to truncate the
word hate because nobody likes
hate.
So we want our text to look like
this, but when we run this code,
we'll get a result like this
with all of the text in
Helvetica 12 and using the
default paragraph style with the
default line break mode of word
wrapping.
And, again, this is really great
for those of you who loathe
Comic Sans because it's been
totally eliminated from the
string but it's wrong.
And it's wrong in a different
way from last time.
And to understand the
difference, let's recall that
attribute fixing happens before
layout and this is where the
system repairs the inconsistent
attributes.
And so here in our attributed
string we have a single
paragraph with multiple
paragraph styles and that's
pretty inconsistent.
So when the system fixes the
attributes of this string, it's
going to take the first
paragraph style it finds and
apply it to the entire
paragraph.
And that's how we ended up with
our attributed string displaying
with the default paragraph
style.
And the key take away here is to
be explicit with your
attributes, especially when
you're mixing plain and
attributed text.
So by doing this, you're going
to avoid this reset effect with
the default attributes.
And for AppKit developers, this
is actually super important if
you're updating your app for
dark mode.
So by using the explicit
attributes with the dynamic
colors like NSColor.textColor,
you'll ensure that your text is
drawn with the correct colors
for the context.
So moving on.
The next topic is performance.
If you're working with large
amounts of text, a good way to
improve your apps performance is
to use noncontinuous layout.
And to understand what that
means, let's revisit our old
friend the layout process.
We said that the layout process
consists of glyph generation
followed by glyph layout.
And so with continuous layout,
the layout manager is going to
perform glyph generation and
glyph layout starting at the
beginning of the text storage.
And it goes in order from the
beginning to the end.
And so if someone using your app
scrolls to some point in the
middle of your text view, the
layout manager has to generate
and layout the glyphs for all
the glyphs that come before that
point as indicated by the red
rectangle.
And note that this also includes
the text that you can't see
that's been scrolled off the top
of the screen all the way back
to the beginning of the text
storage.
And so if you have a lot of
text, that poor person might
have to wait a while for your
app to finish layout but
luckily, we can avoid this
situation by using noncontinuous
layout.
And so as the name implies, with
noncontinuous layout, the layout
manager doesn't have to do glyph
generation and layout in order
from the beginning of the text
storage.
So now when that person, using
your app, scrolls to the middle
of your text view, the layout
manager can perform glyph
generation and layout for that
middle section right away.
So if your text storage has a
lot of text in it, using
noncontinuous layout is a huge
performance win.
Great. So how do you turn this
on?
Well, noncontinuous layout is a
property of NSLayoutManager.
And so for NSTextView, you can
access the text to use layout
manager and then you can set
that property there.
For UITextView, you usually
don't have to do anything
because this is turned on by
default, but there's just one
important thing to remember.
Since UITextView is a subclass
of UIScrollView, noncontinuous
layout will require scrolling to
be enabled.
And this is because when you
disable scrolling, asking for
the intrinsic content size of
your text view is going to
require laying out all the text
and then you wouldn't get the
performance benefits of
noncontinuous layout in the
first place.
And that brings me to a really
important point.
You should avoid requesting
layout for all or most of the
text at once when you're using
noncontinuous layout since that
kind of defeats the purpose of
using it in the first place.
So if you have only one text
container, don't ask for the
layout of the entire thing.
And don't ask for layout for
large ranges of characters or
glyphs that include the end of
the text.
And we didn't dig too deeply
into the topic of text
performance here because I gave
a great talk on this last year
at WWDC 2017, Efficient
unteractions with Frameworks.
And you can access the video
from that more information link
at the end of the session.
All right.
Now it's time to talk about
everyone's favorite topic,
security.
So you may have noticed that
there have been incidents in the
recent past where some people on
the Internet have exploited bugs
in our software to cause
problems for people who use our
products.
And in response, we're
continuing to devise techniques
for mitigating these kinds of
attacks, but today I'd like to
talk about how we can work
together to provide a stronger
defense against these attacks.
So you may have heard of the
concept defense in depth.
And in case you're not familiar
with the terms, defense in depth
refers to creating multiple
layers of protection to defend
against threats.
And this concept has been around
for centuries.
You can see it in the design of
medieval castles.
The land around the outside is
clear of trees so you can see
attackers coming.
And there's a moat to make
approaching the castle more
difficult and to prevent
tunneling underneath it.
And the walls are another
defense.
They're built tall so that
they're difficult to climb.
And there are arrow slits in the
walls and crenellations at the
top to allow defenders to fire
on attackers from protected
locations.
Now any one of these individual
protections might not be enough
to fend off an attack but
collectively they provide a
strong defense.
And like the castle, we here at
Apple provide multiple layers of
defense against attacks, but
there's nothing stopping you
from also taking your own
defensive measures in your app
or framework.
And by doing this, you're adding
another layer of protection and
improving your product security.
Everyone wins.
So let's talk about what you can
do here.
And something I'd like you to
consider is setting limits on
text input in your app or
framework.
And now I'd like to emphasize
that this might not always make
sense to do.
So, for example, if your app is
an authoring tool like that
journal app that Emily showed
earlier, it wouldn't really make
any sense to set a limit on the
length of the text there.
So if it doesn't make sense, you
shouldn't do it.
But in contrast, if your phone
app has a text field for
assigning a nickname to an
account, you probably have some
idea what a reasonable limit
would be there.
And it's a good idea to set
these limits because all text
input is potentially untrusted.
When you allow text input, you
allow copy and paste.
You don't know what kind of text
can be pasted in there.
It could be anything.
It could be a string with
malicious character
combinations, or it could just
be a string that's really,
really, really long.
And even though long strings
like that may not be malicious
in themselves, it could cause
your app to freeze or hang.
So if you have a text field
that's intended for one line of
input and someone pastes the
entire contents of "War and
Peace" into it, which is about
3.1 million characters in
English, is that reasonable?
Probably not.
So this is a great example of a
case where it makes sense to
impose your own limits.
And here are the recommended
approaches for setting these
kinds of limits.
You want to validate the input
string before it's set on the
text field.
And so for UITextFields, you can
do this by using
UITextFieldDelegate.
And for NSTextFields, you should
use a custom NSFormatter to
implement your validation logic.
Oh, and we've also got some
additional security enhancements
coming your way.
So keep an eye out for them in
the release notes and come see
us at the labs this week if you
have any questions.
All right.
We're just about out of time so
let's recap.
You know how to choose the right
control, customization point,
and customization approach and
you know the best practices to
follow in the areas of
correctness, performance, and
security.
So use this knowledge to go
forth and create great things
with TextKit.
Oh, and before you go, here's
that super important more
information link where you can
find all of the great past
sessions and documentation that
we've referenced today.
And come visit us at the labs on
Thursday and Friday.
Thank you and enjoy the rest of
the conference.
[ Applause ]