Frameworks • iOS, macOS • 37:54
Leverage the abilities of TextKit to provide the best experience possible displaying and editing text. Get the best performance out of your app by using TextKit effectively. Learn the concepts to do more complex handling, layout and presentation.
Speakers: Donna Tom, Emily Van Haren
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
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 ]