Writing Tools with Apple Intelligence

Oct 14 2025 · Swift 5, iOS 26, Xcode 26

Lesson 02: Controlling Writing Tools With UIKit

Demo 01

Episode complete

Play next episode

Next
Transcript

In this demo, you’ll see how to configure Writing Tools behavior and output in a sample app. Open the CoverCraft project in this lesson’s starter folder.

Because this lesson focuses on UIKit, you’ll work with CustomTextView.swift, which is a UITextView that conforms to UIViewRepresentable so it can be used in SwiftUI. Begin by commenting out the SwiftUI TextEditor view in CoverLetterView.swift and calling CustomTextView here instead.

CustomTextView(text: $coverLetterText, coverLetter: coverLetter)

// TextEditor(text: $coverLetterText)
// .writingToolsBehavior(WritingToolsBehavior.complete)
// .scrollContentBackground(.hidden)
// .background(Color(UIColor.systemGray5))
// .cornerRadius(8)
// .frame(height: 300)
// .padding(.horizontal)

This changes the text entry area shown when you select Craft Cover Letter to a UITextView, the standard text entry view available in UIKit. The text parameter contains the initial text displayed in the view and binds to whatever the user enters there. The coverLetter parameter gets used to save a draft letter.

Build and run now. Try writing a cover letter draft and you’ll see the app functions just as before, including offering Writing Tools when you highlight text.

Setting Writing Tools Behavior

Next, open CustomTextView.swift. Add the following line just before the return statement in makeUIView():

textView.writingToolsBehavior = .none

The writingToolsBehavior property allows you to specify which Writing Tools interface to display in a text-input view. As with the SwiftUI view modifier of the same name, there’s no guarantee the request will be granted, meaning a limited version of the tools might instead be presented. The .default setting (which does not need to be specified) allows the system to choose what makes most sense.

The line above prevents Writing Tools from displaying in the view. If you build and run now, then draft a letter and highlight text, you will no longer see the tools.

Requesting the Writing Tools Output

Using UIKit, you can also specify which kinds of results Writing Tools returns. This is done via the allowedWritingToolsResultOptions property. To see this in action, first undo what you did in the last section. Inside makeUIView(), set writingToolsBehavior to .complete:

textView.writingToolsBehavior = .complete

Then, add the following line just below the call to writingToolsBehavior:

textView.allowedWritingToolsResultOptions = [.plainText, .richText]

This says Writing Tools can generate plain and rich text but should not return lists or tables. Other options include list and table, both of which imply rich text attributes will be preserved.

Requesting the Writing Tools Experience in a Web View

Like with text views, you can request a preferred version of the tools for the system to display. To do this for a web view, you’ll need to create a WKWebViewConfiguration object, on which you’ll set the same writingToolsBehavior property that’s used on UIKit text views and also available as a view modifier in SwiftUI.

Note: Writing Tools running on web views automatically ignores tags such as <blockquote> and <pre>.

In the starter project for this lesson, open JobPostingView.swift. You’ll see that the view in which the job posting link opens is a WKWebView, defined in the WebView struct.

Replace the current implementation of makeUIView() in the WebView struct with the following:

func makeUIView(context: Context) -> WKWebView {

}

Now add the method body.

let configuration = WKWebViewConfiguration()

This creates a configuration object by calling WKWebViewConfiguration().

if #available(iOS 18.0, *) {
  configuration.writingToolsBehavior = .complete
}

This makes an availability check and then sets the writingToolsBehavior property to .complete so the web view will try to show the full Writing Tools experience.

The writingToolsBehavior property is of type UIWritingToolsBehavior with following options:

  • .none: Writing Tools ignores this view.
  • .default: the system decides which version of the tools to display.
  • .complete: if possible, the full inline-editing experience is displayed.
  • .limited: if possible, the limited overlay experience is displayed.

The default behavior for web views is .limited.

Now, add the following:

  let webView = WKWebView(frame: .zero, configuration: configuration)
  return webView

This calls WKWebView to return the web view with the requested configuration. The frame parameter is required but unused here because SwiftUI will later provide the actual size and positioning for the web view when it lays out its views.

Build and run CoverCraft. Select Research an Opportunity, then try pasting a job link (or any URL) into the link area.

When the web view loads the text, select some or all of it and choose Writing Tools from the Edit menu. Experiment with changing the setting of writingToolsBehavior in makeUIView() and see how that affects the experience.

Reading the Current Writing Tools Behavior

You can also read the current Writing Tools behavior in web and UIKit text views. Start by making sure the logic inside the availability check in makeUIView() looks like this. It might be different depending on where you left in experimenting in the previous step:

if #available(iOS 18.0, *) {
  configuration.writingToolsBehavior = .complete

}

Again, this checks to make sure the app is running on iOS 18 or later before using features available only there. It then requests the complete Writing Tools experience. Add the following to get the current behavior:

let currentBehavior = configuration.writingToolsBehavior

This stores the current Writing Tools behavior in the currentBehavior variable. Add the following:

  let behaviorName = [
    UIWritingToolsBehavior.none: "none",
    UIWritingToolsBehavior.default: "default",
    UIWritingToolsBehavior.complete: "complete",
    UIWritingToolsBehavior.limited: "limited"
  ][currentBehavior] ?? "unknown"

This creates a dictionary to map each UIWritingToolsBehavior enum case to a corresponding string name and then uses [currentBehavior] to look up the current behavior in the dictionary.

Finally, print out the current behavior to the console:

print("Current Writing Tools Behavior: \(behaviorName)")

Build and run, then tap Research an Opportunity. Paste in a link and have a look at what displays in the console.

Continue to the next segment to learn some advanced configuration options for a UITextView, including how to set a range of text the tools should ignore and how to pause and resume app operations so you don’t end up with unexpected results in stored text.

See forum comments
Cinema mode Download course materials from Github
Previous: Instruction 01 Next: Instruction 02