Touch on Mac Docs: Migration Guide, Games Technote, HIG
What did Apple publish for Touch on Mac after the talks? By the evening of October 28 the AppKit article “Adopting gesture recognizers in your app,” which returned 404 at noon, was live with a four-row table that gives every kind of mouse-handling code a next step; a second article, “Handling AppKit gestures,” documents exclusive views, a two-second timeout for stuck recognizers and two debugging defaults; a new technote, TN3216, covers games and the order of emulated mouse events; and three Human Interface Guidelines pages carry change-log entries dated October 28 for touch on the Mac.1234 The landing page’s “Documentation resources” tile now links the article and the file-promises sample, its “Design resources” tile links the three HIG pages, and the Xcode 27.3 beta tile still says “Coming soon.”5
TL;DR: The developer post, published at 1:47 p.m. PT on October 28, worked from three Tech Talks and a June technote; the AppKit article it needed returned 404 at noon. By 9:51 p.m. that article was live with two more documents beside it. The migration article sorts your code into four rows: standard controls and recognizers need nothing; mouseDown(with:) overrides and tracking loops get single-finger emulation and should become recognizers; a control subclass that overrides a mouse method falls back to a tracking loop; a custom recognizer that never finishes holds its exclusive view.1 The rule behind the third row is that AppKit decides by whether the method is overridden, “not on what the override does,” so calling super does not help.1 A touch-only NSPanGestureRecognizer with buttonMask 0 beside a loop you keep is now documented, with the precise consequence of leaving the mask alone: a mouse drag starts the recognizer and your mouseDown(with:) never runs.1 Exclusive views are on by default when the Info.plist key is absent, and Xcode’s macOS App template writes the key as NO only for a project whose app lifecycle is the AppKit app delegate, not for the default SwiftUI lifecycle; I read that in the templates inside Xcode 27.1 and the 27.2 beta on this Mac.27 A stuck recognizer is canceled two seconds after the interaction ends, and while you debug one user default crashes on a missing override and another replaces the silent cancel with a crash.2 The games technote lists the exact mouse events a tap and a drag produce, tells engines not to add up deltas, and explains that each touch surface appears as an additional emulated GCMouse until NSIsTouchNative is set.3 The HIG calls touch “a secondary input mode,” says to make “tap targets bigger, not the controls themselves,” and adds six rules for controls that only appeared on hover.4 Still not here: the Xcode 27.3 beta and the three system-gesture switches, which return 404 at 9:51 p.m. PT.56
What landed, and when
Each item below carries the time I checked it, because the server’s own stamps disagree with what the pages showed during the day; the live post has the day’s record, with a late row for this drop.
- The AppKit article. “Adopting gesture recognizers in your app” returned 404 from Apple’s documentation endpoint at noon PT; at 9:51 p.m. it was live, served with a modification stamp of 2:30 p.m. PT, listed in AppKit’s Essentials section, after “Adopting Liquid Glass,” and under “Gesture handling” on the framework’s Gestures page, and the Wayback Machine’s first capture of it is 6:08 p.m. PT (receipts in the sources).1 The Tech Talk page for “Bring Touch to your Mac app” still does not link it.5
- A second article. “Handling AppKit gestures” sits beside it on the Gestures page. It has no revision history, the June technote does not link it, the Wayback Machine has no capture of it, and its server stamp is 10:35 p.m. PT on October 27; it was not among the pages I checked at noon, so I cannot say when it went up.2
- A new technote. “TN3216: Supporting touch input for games on macOS” has a revision history of one line, “2026-10-28 First published,” and a server stamp of 2:09 p.m. PT.3
- Three HIG pages. “Designing for macOS,” “Gestures” and “Pointing devices” now end with change-log rows dated October 28, 2026. The copies I fetched at noon had none of the new text; the copies fetched at 9:51 p.m. do. The server’s modification header on the new “Gestures” page reads 5:11 UTC on October 27, earlier than the noon copy that lacked the text; I report both facts and no publication minute.4
- The landing page. At 11:49 a.m. the three tiles under “Tools and documentation” all read “Coming soon,” with their links held in HTML comments. At 9:51 p.m. the “Documentation resources” tile (“Learn how to build and optimize your app for touch input.”) links the article and the file-promises sample, and the “Design resources” tile (“Create experiences that take advantage of touch user interface patterns.”) links the three HIG pages and Apple Design Resources, whose product-bezel list includes “MacBook Pro PROLED XDR Display.” The “Xcode 27.3 beta” tile still reads “Coming soon,” and the HTML comment
TK skillis still in the page.5 - The sample. The page for “Supporting drag and drop through file promises” now says of its recognizers that “The recognizers work the same way with a pointer or with direct touches on a Mac with a touch display.”6
- Nothing else moved. The developer news feed’s newest item is still “Say hello to Touch on Mac” at 10:00:55 a.m. PDT; the releases feed’s newest items are the October 26 point releases, with no Xcode 27.3 or macOS 27.3 beta; the Tech Talks listing has the same three talks; the ActivityKit page carries an October 8 stamp and still lists no macOS platform.56
The afternoon post said the written rules were the talks and the June technote. This post is about what the written rules add, and it corrects the afternoon post where the evening check found different information; the list is at the end.
The table that triages your code
The article’s “Prepare your app for a Mac with a touch display” section is a table with three columns, input handling, behavior with touch, and next step, and four rows. Apple’s behavior column is quoted; the other two columns are condensed, except the third row’s next step, which is Apple’s wording:1
| Your code | Apple’s “Behavior with touch” | Next step |
|---|---|---|
| Standard AppKit controls and gesture recognizers | “Works with touch, with no changes needed.” | Test. |
mouseDown(with:) overrides and tracking loops |
“AppKit emulates a single-finger tap or drag, but supports multi-finger gestures and individual touches only through gesture recognizers.” | Replace them with recognizers. |
Subclasses of NSButton, NSSlider and other AppKit views that override mouse-handling methods |
“AppKit falls back to a tracking loop.” | “Replace the overrides with target-action pairs for tracking events.” |
Custom NSGestureRecognizer subclasses |
“A recognizer that never reaches a terminal state keeps its exclusive view, which can make your app appear unresponsive.” | Handle cancellation and reach a terminal state. |
The test that follows the table is four lines, worth running as written: “Controls respond to a tap, and buttons highlight and activate as expected,” “Dragging a view, selecting text, and scrolling work with one finger,” “Pinch and rotate work where your app supports them,” and “Small targets, such as resize grips and grab handles, are easy to hit with a finger.”1 The search list extends the talk’s audit of mouseDown(with:) overrides to five symbols: “search your project for nextEvent(matching:until:inMode:dequeue:) and trackEvents(matching:timeout:mode:handler:), because tracking loops typically call these methods. Also search for mouseDown(with:) and mouseDragged(with:) overrides, and for uses of currentEvent in control action methods.”1 The last check is the one the fourth row is about: “confirm that your app stays responsive after a touch interaction ends, and that every gesture recognizer action method handles the NSGestureRecognizer.State.cancelled state.”1
Two sentences in the Overview settle a question the talk left to inference. Emulation is single-finger: “AppKit emulates mouse events from a single finger tapping or dragging,” and “Relying on this emulation, however, means you miss out on multi-finger gestures, like pinch and rotate, that gesture recognizers handle.”1 And the test rig is the one the talk named, now in a tip: “Use Sidecar to test touch input without a Mac that has a touch display,” because “AppKit then routes touch on its screen the same way it does on a Mac with a touch display.”1
The override rule
The third row of the table has a rule behind it that neither the talk nor the June technote stated this plainly. If you subclass NSButton and override mouseDown(with:) to add a highlight animation, “AppKit treats your override as a replacement for its own click handling, rather than as a side effect,” and the button goes back to a tracking loop.1 Then: “AppKit decides based on whether your subclass overrides the method, not on what the override does,” so the fallback applies even when the override calls super.mouseDown(with:), and “Overriding mouseDragged(with:) or mouseUp(with:) has the same effect.”1
The replacement is the control-events pattern from the talk, and the article’s listing differs from the talk’s in one detail worth copying. The talk’s 2:24 listing registered one action for [.trackingExited, .trackingEndedInside] and a separate one for .trackingCancelled; the article’s hide set is [.trackingExited, .trackingEndedInside, .trackingEndedOutside, .trackingCancelled], which adds a press that ends outside the button.19 trackingEndedOutside is not new: Apple’s documentation lists it at macOS 11.0, and the macOS 27.0 SDK declares it as NSControlEventTrackingEndedOutside = 1 << 7 in NSControl.h (author analysis).67 The article also notes that the action methods must be exposed to Objective-C with @objc, and that if you only need the activation, primaryActionTriggered fires “regardless of whether someone used a mouse, trackpad, or touch to produce it.”1
Container views have the same trap: “If you start a drag from mouseDragged(with:) in an NSTableView, NSCollectionView, NSOutlineView, or NSBrowser, AppKit falls back to a tracking loop for the interaction,” and the way out is the data source and delegate dragging methods, tableView(_:pasteboardWriterForRow:) for a table.1
For the two common mouseDown(with:) overrides the article gives before-and-after pairs. “Most mouseDown(with:) overrides do one of two things: mark an item as selected when someone clicks it, or show a menu when someone Control-clicks.”1 For selection, observe isSelected on the collection view item or table row view instead, with the caveat that the observer “also runs when your code changes the selection programmatically”; for the menu, override menu(for:) and let AppKit present it, or set menu or defaultMenu when the menu does not depend on the click.1
A touch-only recognizer beside a loop you keep
The talk recommended a touch-only pan recognizer beside an existing tracking loop as the dot-release path. The article documents the same recipe under “Add touch support to your custom tracking loop,” with the shared start, continue, end and cancel functions, and then states what each property does with a precision the talk did not have room for:1
let touchRecognizer = NSPanGestureRecognizer(target: self, action: #selector(handleTouchPan))
touchRecognizer.buttonMask = 0
touchRecognizer.allowedTouchTypes = .direct
addGestureRecognizer(touchRecognizer)
“setting buttonMask to 0 means the gesture recognizer only recognizes touches, not mouse or trackpad input, so your tracking loop keeps handling the mouse and trackpad exactly as before.”1 Leave the mask at its default and the recognizer “also responds to the primary mouse button. A mouse drag then begins the gesture recognizer, and AppKit doesn’t call your mouseDown(with:) override, so the tracking loop doesn’t run. A click that doesn’t move still reaches your tracking loop.”1 So the mask is not a nicety; without it the loop silently stops receiving drags. The allowedTouchTypes line is belt and braces: “Direct touches are already the default for allowedTouchTypes, and setting it to .direct states that intent explicitly.”1 Apple’s own assessment of the approach: “This is often the fastest, lowest-risk way to add touch support to code with a tracking loop you’d rather not rewrite all at once.”1
If you replace the loop instead, the article’s pan example reverts the caption to its start origin on .cancelled, and a loop that started a drag session passes the recognizer to beginDraggingSession(items:gesture:source:) instead of the event.1
Filtering with the two delegate methods
The section that is most new relative to the talks is “Filter gesture recognizer candidates.” Recognizers are gathered by hit-testing, not the responder chain: “Gesture recognizers don’t use the responder chain.”1 When AppKit gathers them, “not every recognizer it finds is actually applicable to the interaction,” and the app removes the inapplicable ones through two delegate methods: gestureRecognizer(_:shouldAttemptToRecognizeWith:) “to inspect the initiating NSEvent for pointer-based input,” and gestureRecognizer(_:shouldReceive:) “to inspect each new NSTouch for touch input.”1 The point of both: “Either method gives you the initial input, something the recognizer doesn’t otherwise expose, and lets you stop the recognizer from handling that event stream or touch.”1 And the split is strict: “Direct touches don’t call this method. To filter them, implement gestureRecognizer(_:shouldReceive:) instead, as shown above.”1 Filtering only through the event method leaves direct touches unfiltered by that delegate.
The two examples are patterns I expect to see copied. A resize grip: attach the resize recognizer to the whole view, and in both delegate methods return whether the location is inside the grip’s frame, returning true “only to give the gesture recognizer a chance to attempt recognition, not to guarantee it succeeds.”1 A timeline of clips: one recognizer on the timeline, and in the event method hit-test the clip, store it for the action method, and return false when there is none.1 The companion warning for small targets: “If a custom control expects a touch to land inside its visible area, update it to handle touches that land outside that area.”1
Priority between the survivors is a failure requirement, and the article repeats the warning from the second article, “Use this sparingly, because the waiting recognizer responds more slowly,” and gives the example that “making a single click wait for a double click to fail makes clicks feel sluggish.”1
currentEvent, hover and swipes
“Handle touch input” restates the organizing sentence from the talk in documentation form: AppKit “doesn’t deliver direct touches (NSTouch) through the responder chain or through event monitors,” and “Gesture recognizers are the only way to respond to multi-finger gestures or to receive individual touches from a Mac with a touch display or a Sidecar-connected iPad.”1 The AppKit input overview page says the same from the responder side: “The responder chain receives touch events only from indirect touches on a trackpad, if a view asks for them. It doesn’t receive touch events from direct touches on a Mac with a touch display.”6
The advice on capability checks is to avoid them: “In most cases, instead of checking whether a device supports touch, write your event-handling code to work the same way regardless of input source, and decide on alternate presentations based on the interaction type.”1 The one example given for touchCapabilities is “capturing a signature on a touch display.”1
currentEvent gets its own section. Because control interactions go through recognizers, “currentEvent no longer reliably returns the event that initiated an action,” and the before-and-after pair replaces the event’s modifier flags with the class-level NSEvent.modifierFlags, with the caveat that “These report the state at the time you query them, not at the time of the original event. A touch doesn’t register in pressedMouseButtons.”1 What does not change: “Keyboard events and scroll-wheel events behave the same on every input source: AppKit delivers them through the responder chain. Hover tracking, which relies on mouseMoved(with:) and NSTrackingArea, applies to pointer input only, because a touch display has no hover.”1
Swipes are not translated: “AppKit doesn’t translate touch swipes for you.”1 If you use trackSwipeEvent(options:dampenAmountThresholdMin:max:usingHandler:), implement your own, and for back-and-forward navigation check NSEvent.isTouchSwipeNavigationEnabled first, “because the person can turn off swipe navigation in System Settings.”1 That property is new: Apple’s documentation lists it at macOS 27.0, and NSEvent.h in the macOS 27.0 SDK declares it at line 496 as API_AVAILABLE(macos(27)) (author analysis).67
Custom recognizers: the contract, the timeout and two defaults
The article’s guidelines for a custom NSGestureRecognizer subclass are a contract in seven bullets: handle cancellation in mouseCancelled(with:) and touchesCancelled(with:); in the event methods only update private tracking state and set state; respond in the action method, not the event callback; cache the originating NSEvent or NSTouch for the action method; “Override reset() to clear your cached state, so it doesn’t leak from one gesture into the next”; “Always end an active gesture recognizer by transitioning to NSGestureRecognizer.State.ended, NSGestureRecognizer.State.failed, or NSGestureRecognizer.State.cancelled. Otherwise, other recognizers can’t activate”; and override location(in:), because “If you skip this override and build your app with the macOS 27 SDK and later, AppKit returns NSZeroPoint and logs a console error.”1 The DragTrackingRecognizer example that follows is a scaffold: the bodies of its mouse-down, dragged and up overrides are comments telling you to call one private track(_:event:state:) method, the cancel override’s to set .cancelled, and the action method reads the cached location and event back.1
The second article is where the consequences of breaking the contract are quantified. “A gesture recognizer is stuck if it hasn’t reached a terminal state within 2 seconds after the person stops interacting with your app, such as when they release the mouse button or lift their fingers.”2 Under exclusive views, “A stuck gesture recognizer keeps holding its exclusive view,” which blocks gathering outside its hierarchy, and the system’s remedy is automatic: “To keep a single stuck gesture recognizer from blocking your app indefinitely, the system automatically transitions it to the NSGestureRecognizer.State.cancelled state, allowing gesture gathering to resume normally.”2 That is the mechanism behind the fourth row of the table, and it means a broken recognizer in a shipping app blocks gesture gathering outside its exclusive hierarchy, the rest of the app appearing unresponsive, until the automatic cancel two seconds after the person stops interacting, with the action’s target still responsible for handling the cancel.
Two user defaults make these problems crash with a stack trace while you develop, one for a missing override and one in place of the silent cancel. “Set the NSGestureRecognizerCrashOnMissingOverrides key in UserDefaults to YES,” or pass -NSGestureRecognizerCrashOnMissingOverrides YES as a launch argument, to crash when an event method you should have overridden is missing; and set NSCrashOnStuckGestureTimeout “to a small number of seconds, such as 2,” or pass -NSCrashOnStuckGestureTimeout 2, to crash instead of canceling a stuck recognizer.2 The first does not catch everything: it “doesn’t detect a gesture recognizer that correctly transitions to NSGestureRecognizer.State.cancelled and sends its action, if the action’s target doesn’t handle the canceled state.”2 The warning that follows is the kind that belongs in a checklist: “Comment out or remove NSGestureRecognizerCrashOnMissingOverrides and NSCrashOnStuckGestureTimeout before shipping your app,” and “Changes to these user defaults or launch arguments take effect only after you rebuild or relaunch your app.”2 Neither default appears in the talks or the June technote.
The same article explains the difference between the two terminal states that are not success: .failed is for a recognizer that never matched, and “If a gesture recognizer sets this after it already delivered an action, the system automatically converts it to NSGestureRecognizer.State.cancelled instead.”2 It also names a cause of system cancellation beyond the Exposé example in the talk: “when the same touches begin a system-level gesture like smartMagnify(with:), the system takes over those touches for its own use.”2
Three smaller facts from the same page. Recognizers can delay responder events: “If you set delaysPrimaryMouseButtonEvents to true, the system stores the relevant events until the gesture recognizer fails, then delivers them to the appropriate responders. If the gesture recognizer reaches the NSGestureRecognizer.State.ended state, the system never delivers the events.”2 And the responder’s own touch methods are trackpad-only: “These methods don’t have an equivalent delay mechanism, and they apply only to indirect touches from a trackpad.”2 A view controller cannot hold a recognizer: “You can’t attach a gesture recognizer to a view controller directly,” so add it to the controller’s view with the controller as target and delegate.2
Exclusive views: on by default, off in Xcode’s AppKit templates
The afternoon post explained exclusivity from the talk and the June technote. The two articles add the default, the mechanism and an Xcode fact.
The mechanism, new in macOS 27: “In macOS 27 and later, the initial hit-tested view for a mouse click or touch event becomes an exclusive view for gathering gesture recognizers,” and “This behavior simulates the presence of a single mouse pointer, even when a person touches a Mac with a touch display with several fingers at once.”2 The default: “Exclusive views are on by default,” and “If your app’s information property list doesn’t include the NSViewGestureRecognizerIsExclusive key, the system treats it as YES.”2 The key’s own page agrees, “The default value is YES,” and frames the opt-out narrowly: “Set this key to NO to allow gesture recognizers in multiple views to activate simultaneously. Do this only if your app intentionally needs concurrent gesture recognition across different views.”6 The system’s example of opting out per view is the Color Panel: “AppKit uses this same technique in the standard Color Panel,” where several sliders move at once.2
The Xcode fact, stated in both articles: “Xcode adds the NSViewGestureRecognizerIsExclusive key with a value of NO to new macOS app projects, so those apps don’t use exclusive views unless you change the value to YES.”1 I read the project templates on this Mac to see which projects that covers (author analysis). In Xcode 27.1 (27A9275) and in the Xcode 27.2 beta (27B5019j), the macOS App template inherits from a “Cocoa App Base” unit whose app-lifecycle option has two values, SwiftUI and Cocoa, with SwiftUI the default; the key INFOPLIST_KEY_NSViewGestureRecognizerIsExclusive with the value NO sits inside the Cocoa unit only, so Xcode writes it, as a build setting that lands in the generated Info.plist, when the project uses the AppKit app delegate lifecycle and not when it uses the SwiftUI lifecycle.7 The lifecycle option carries no display name in the templates; the option that does is Interface: (SwiftUI, Storyboard or XIB, default SwiftUI), and the Storyboard and XIB units require the AppKit lifecycle, so a Storyboard or XIB project gets the key, and a SwiftUI-interface project gets it only when its lifecycle is the AppKit app delegate.7 The Document App template descends from the same base, so under the same condition it inherits that build setting and also writes the key as an Info.plist entry with the value false; the macOS Game template forces the Cocoa lifecycle, so a new macOS game starts non-exclusive.7 No Xcode older than 27.1 is on this Mac, so I cannot say when the templates gained the key. The practical reading: an AppKit project created from these templates starts non-exclusive; a SwiftUI project, and any existing app whose Info.plist has no key, is exclusive. The second article’s migration note follows from that: if your app set the key to NO, “also set the view’s exclusiveGestureBehavior to NSView.ExclusiveGestureBehavior.exclusive” when you replace a tracking loop, because “This prevents simultaneous gesture recognition with unrelated views, which the loop previously prevented implicitly.”2
The first article untangles the three settings that touch exclusivity and scrolling, with the sentence “These settings don’t override each other.”1 exclusiveGestureBehavior and the plist key decide which recognizers AppKit gathers, and “The view’s setting applies to that view and its subviews, and takes precedence over the app-wide key.”1 isCancellableByScrollGesture is per recognizer and independent: “It lets a scroll gesture cancel that gesture recognizer even if it has already begun.”1 scrollGestureForRelationships is for recognizers that compete with the scroll view’s own.1 The scenario the article says to watch for is a button inside a scroll view: set isCancellableByScrollGesture to true on its recognizer so a drag that starts on the button scrolls instead of clicking.1
Text engines get one more tool. NSTextSelectionManager “creates its gesture recognizers itself, so you don’t have a reference to them,” so a custom recognizer that must wait for them returns true from gestureRecognizer(_:shouldRequireFailureOf:) when the manager’s gesturesForFailureRequirements contains the other recognizer.1 That property is listed at macOS 27.0 and declared in NSTextSelectionManager.h at line 59 of the macOS 27.0 SDK (author analysis).67
Emulation, in five bullets
The afternoon post quoted the June technote’s four emulation rules for non-AppKit UI frameworks. The article’s list has five, and the new one is the drag.18
- “Tapping emulates a mouse-down followed immediately by a mouse-up.”
- “Touching and then immediately panning emulates a trackpad scroll, even if the UI doesn’t otherwise handle Multi-Touch input directly.”
- “Touching, briefly holding, and then moving emulates mouseDown(with:), followed by mouseDragged(with:) as it moves, and mouseUp(with:) on lift.”
- “Touching and holding without movement emulates rightMouseDown(with:), and rightMouseUp(with:) on lift.”
- “Pinching or rotating with two fingers emulates the trackpad magnify and rotate gestures.”
The third bullet is the written form of the talk’s “drag with a small hold delay” (11:31): a touch that moves at once scrolls, a touch that holds first drags.9 The June list had a tap, an immediate pan, a long press and a two-finger pinch or rotation, and no drag bullet.8 The limit is two fingers: “This emulation covers interactions with one or two fingers. If another finger touches down during a pinch or rotation, the emulation ignores it and keeps tracking the original two fingers.”1 Three or more fingers need recognizers, and NSIsTouchNative set to YES turns off the extra emulation once the framework handles touch itself.1
Games: TN3216
The technote is for games: “This guidance focuses on configurations common in games, such as an app with a single view backed by a CAMetalLayer.”3 Its design framing is stricter than the HIG’s: “Touch extends the Mac experience rather than replacing other input,” and “Touch works best for brief interactions that complement these devices,” with the instruction to “Design touch interactions for brief moments that the player can complete with one hand, while the other hand stays on the keyboard, mouse, or controller,” for interactions “such as inventory management, map zoom and scroll, or the settings menu.”3
The compatibility story is the same emulation with a games twist. “AppKit applies game-specific emulation to apps in a games category, and can apply emulation to an individual view, such as one that hosts a third-party UI framework.”3 The category is read from Info.plist: if LSApplicationCategoryType is public.app-category.games or a game subcategory, “the system applies game-specific mouse emulation.”3 The problem it solves is the one a mouse never has: “A mouse moves continuously from one position to the next, but a player can tap or start a drag at two distant locations in succession,” so a game that adds up deltas drifts, and “For games, AppKit sends a mouseMoved event to the new location before each mouseDown(with:), so your game can refresh its cached mouse position.”3
The technote then lists the emulated events in order. For a tap: mouseMoved(with:) “from the last mouse position to the tap location,” then mouseDown(with:), then mouseUp(with:). For a drag: mouseMoved(with:) to where the drag begins, mouseDown(with:), mouseDragged(with:) “repeatedly while the touch continues,” and mouseUp(with:).3 Then a rule in an “Important” box: “Don’t intercept events by overriding sendEvent(_:) or pulling them with nextEvent(matching:until:inMode:dequeue:). Doing so can interfere with gesture recognizers that respond to a physical mouse.”3
The part with no counterpart in the talks is GCMouse. “By default, the system presents each touch surface, whether a Mac with a touch display or a Sidecar-connected iPad, as an additional GCMouse that emulates mouse input from touches”: a finger down presses the left button, movement calls the mouse-moved handler with a delta, lifting releases.3 Its limits: “The emulated GCMouse ignores additional fingers, and it doesn’t emulate other buttons, scrolling, or pinch input,” and because touches land in disconnected places, “Don’t use them to track an absolute pointer position.”3
NSIsTouchNative does more for a game than for an AppKit app. Set to YES, “AppKit’s emulation will stick to a generic emulation - ignoring the app’s category or whether it uses a third-party UI framework - and disable GCMouse emulation,” so the touch surface no longer appears as a GCMouse and “Your game receives touch through gesture recognizers instead of emulated mouse events.”3 One residue remains: “AppKit still emulates mouse events for any NSView that handles them, for example by overriding mouseDown:.”3 The key’s own page says most AppKit apps do not need it and that it is “primarily intended for apps that embed a non-AppKit rendering or input framework and implement their own touch-handling pipeline”; a game engine is that case.6
The implementation section is a delegate pattern in Swift and Objective-C: a pan and a tap recognizer with buttonMask 0 so they ignore a physical mouse, a magnification recognizer, shouldRecognizeSimultaneouslyWith returning true only for the pan and pinch pair, and shouldRequireFailureOf making the tap wait for both.3 A note confirms the default the first article states: “The gesture recognizers must include direct in allowedTouchTypes. On macOS 27 and later, this is the default.”3 The routing advice is engine-shaped: “Install the gesture recognizers once in your app and leave them enabled,” convert callbacks into engine input events, and “Let the active input handler decide if and how to react to different gestures,” with a pan handler that converts points to pixels with the backing scale factor and flips the y-axis for an engine whose origin is top-left.3 Standard controls keep working after the switch, “as long as you don’t attach your own direct-touch gesture recognizers to them.”3
The closing checklist has two halves. For a game on emulation: read the location from each event, and do not override sendEvent: or pull from nextEventMatchingMask:. For a game handling touch: set NSIsTouchNative, use direct-touch recognizers with buttonMask 0, add touch where players expect it and keep it one-handed, “Continue to read GCMouse for the physical mouse. With NSIsTouchNative set, touch no longer appears as an emulated GCMouse,” and use standard controls where possible.3
The HIG, dated October 28
The afternoon post said the four HIG pages the landing page planned to link contained no guidance for touch on the Mac display. Three of them do now, each with a change-log row dated October 28, 2026; the Live Activities page is unchanged.4
Designing for macOS (“Added guidance for touch input.”) rewrites three lines of its platform description and adds one best practice. Ergonomics: “On a Mac with a touch display, people reach forward to touch the screen, so they tend to use touch for brief, intentional interactions rather than sustained work.” Inputs now lead with keyboards and pointing devices and list “Gestures on a trackpad or a Mac with a touch display” among the others. The new best practice is the whole design talk in one sentence: “Support standard gestures on trackpads and Mac devices with a touch display. Use larger tap targets and controls that expand on touch to make touch interaction easier, while keeping control size and density the same to stay optimized for keyboard and pointing device input.”4
Gestures (“Added guidance for gestures and touch input in macOS.”) expands its macOS section, which now links both AppKit articles for developer guidance. Its first sentence sets the rank: “On a Mac with a touch display, or on an iPad connected with Sidecar, people can also interact directly using touch gestures as a secondary input mode.”4 Then six rules, each a bold lead:
- “Keep the layout and size of interface elements the same when you support touch.” The reason: “In macOS, touch is secondary to keyboard and pointing device input,” and people switch between inputs often. The contrast with iPhone Duo, where the inner display changes size classes, is the point: the Mac’s input changes and its layout does not.
- “Make tap targets bigger, not the controls themselves,” by “enlarging their tap targets to anticipate that interaction may start slightly outside of their visual bounds,” and “Where possible, use standard framework controls, which the system configures with an optimal tap target size automatically.”
- “Consider providing expandable controls,” a temporary touch-sized variant that appears on tap, stays while held, and remains “for a short time after they lift their finger to keep the control accessible to touch”; “When using standard system controls, steppers, sliders, and dials gain this behavior automatically.”
- “Don’t use touch as the only way to access controls or features,” and the converse: “aim to provide touch equivalents for actions otherwise performed exclusively with a pointing device, like using a keyboard modifier to shift-drag or displaying a menu when the pointer moves over an element.”
- “Where possible, align with gestures people already know,” from your iPhone or iPad app or from the trackpad, and “Test to ensure they don’t conflict with macOS system gestures.”
- “Make sure custom gestures work for both touch and trackpad,” using “modifier keys where needed to help the gesture work for all touch inputs.”4
The page does not give the 36-point figure; that number is in the two talks (“Touch on macOS uses a touch target of 36 points,” 9:17 in the design talk, and the AppKit talk’s hit-rect expansion to “a minimum of 36 points on each axis,” 5:53).9 It adds a table, “macOS trackpad and touch display gestures,” that pairs each action with its trackpad and touch forms: select is a primary click or a tap; open is a double click or a double tap; scroll is two fingers on the trackpad and one on the display; zoom and rotate are the same pinch and two-finger turn on both; a context menu is a secondary click or a touch and hold; a drag is click and drag, or “Touch and hold, then drag.”4 In the cross-platform table, “Touch (or pinch) and hold” now lists macOS, and the double-tap row adds “open an item on a Mac with a touch display.”4
Pointing devices (“Added guidance for revealing hidden controls with touch on Mac, and updated guidance and artwork for iPadOS and macOS.”) is the page with the most concrete rules, under a new heading, “Touch support for hidden controls.” The premise: “Some Mac apps reveal certain controls only when the pointer moves over them. These controls need another way to appear when someone uses touch on a Mac that supports it.”4 The rules:
- “Decide which hidden controls need touch support. Reserve touch support for essential controls.” Tooltips do not qualify, and neither does anything touch already does another way: “someone can swipe to scroll a carousel, so there’s no need to reveal the navigation buttons that exist for mice without a scroll wheel.” And: “Avoid keeping hidden controls visible all the time just to accommodate touch.”
- “Reveal a hidden control when someone taps its general location on a touch display.” Only the relevant ones, and “Hide a control again when the person finishes with it, or after a few seconds if they don’t use it.”
- “Retain the current selection when revealing a hidden control,” because the control may act on the selection.
- “Make essential hidden controls available in a context menu,” which touch opens with a touch and hold.
- “In a sidebar, reveal hidden controls when someone taps the corresponding section header or row.” The example is Finder: “tapping the Favorites header in the Finder sidebar collapses the section and reveals a disclosure button.” The revealed controls stay until touch leaves the sidebar or a pointing device is used, and a tap on a hidden control’s area reveals it without activating it: “The control acts only when they tap it again.”
- “In a tabbed window, reveal the selected tab’s close button when someone taps anywhere in the window,” and keep it until the person goes back to a pointing device.4
Those six rules are the written form of the two talks’ hover guidance, with timings and exceptions the talks did not give: a few seconds before a revealed control hides, a second tap to activate, and sidebars and tab close buttons as the cases that stay visible. The same revision rewrote the page’s iPadOS pointer material for Liquid Glass, changed the default pointer shape in its text from a circle to an arrow, replaced the cursor table’s AppKit-only column with AppKit and SwiftUI symbols, and removed the pointer-magnetism section; none of that is about the Mac display.4
What is still not here
Checked at 9:51 p.m. PT on October 28, against Apple’s documentation endpoint, the landing page, the developer news and releases feeds and the Tech Talks listing.56
- The Xcode 27.3 beta. The landing page tile still reads “Coming soon,” the releases feed lists nothing newer than the October 26 point releases (macOS 27.2 among them), and the Xcode 27.3 and macOS 27.3 release-notes pages return 404. The newest Xcode remains the 27.2 RC, whose SDK I have not inspected; the afternoon post’s deployment-target caveat still applies, and the Xcode 27 release post has the background on deployment targets.
- The three system-gesture switches the AppKit talk demonstrated,
NSWindow.disablesDirectTouchSystemGestures,UIViewController.prefersDirectTouchSystemGesturesDisabledand SwiftUI’sdirectTouchSystemGesturesDisabled(). All three return 404, and neither new article mentions them. The articles describe how a system gesture cancels your recognizer; they do not describe turning the system gesture off. - A macOS-available ActivityKit. The framework page carries an October 8 stamp and lists no macOS platform, and
AlertConfiguration.announcement(_:)from the Live Activities talk returns 404. The afternoon post’s section on Live Activities stands as written, and the state-machine post remains an iOS implementation only. - The “gesture modernization skill” the AppKit talk mentioned at 12:16. No Apple page names it; the landing page’s
TK skillcomment is still there.59 - A link from the talk page to the article, and any Apple statement about a simulator or Device Hub path for Mac touch.
- A dated entry on the SwiftUI documentation updates page. Its server stamp is 2:58 p.m. PT on October 28, but its newest section is still September 2026 and its gesture entry is the June 2026
GestureInputKindsline; whatever changed, it was not a new dated section.6
Corrections to the developer post
The afternoon post was accurate to the noon checks; five of its statements no longer held at the 9:51 p.m. PT check on October 28. An update block in that post, published together with this one, links here, and the statements are corrected in place; this is the list.
- The landing page’s three “Coming soon” tiles are now one. Documentation resources and design resources are live; the Xcode 27.3 beta is not.5
- “Adopting gesture recognizers in your app” no longer returns 404. It is live, and it is the document the migration guidance should be read from.1
- The HIG pages are no longer silent on touch for the Mac display. Three of the four carry October 28 change-log rows; the Live Activities page does not.4
- The emulation list has five rules, not the June technote’s four; the hold-then-drag rule is the addition.18
- Exclusive views are on by default only for apps whose Info.plist lacks the key. Xcode’s macOS App and Document App templates, in Xcode 27.1 and the 27.2 beta, write the key as
NOfor a project whose app lifecycle is the AppKit app delegate.127
What the afternoon post said about the SDKs, the three undocumented switches and ActivityKit is unchanged by anything published since.
Key Takeaways
For AppKit developers: run the article’s four-line test on Sidecar, then search for the five symbols it names; an override of mouseDown(with:), mouseDragged(with:) or mouseUp(with:) on a control subclass drops that control to a tracking loop whether or not it calls super, and the fix is target-action pairs on NSControl.Events with .trackingEndedOutside in the hide set.1
For a loop you keep: the touch-only recognizer needs buttonMask 0, or a mouse drag starts the recognizer and your mouseDown(with:) never runs; allowedTouchTypes = .direct is already the default.1
For custom recognizers: reach a terminal state, override reset() and location(in:), and debug with -NSGestureRecognizerCrashOnMissingOverrides YES and -NSCrashOnStuckGestureTimeout 2, removed before shipping; in production a stuck recognizer is canceled two seconds after the person stops interacting.12
For exclusivity: an app without the key is exclusive; a project created from Xcode’s macOS App template with the AppKit app delegate lifecycle (every Storyboard or XIB project) is not, and one with the default SwiftUI lifecycle is. Read your Info.plist before migrating a tracking loop.27
For games: read locations from events rather than summing deltas, do not override sendEvent(_:), and know that each touch surface is an additional emulated GCMouse until NSIsTouchNative is YES.3
For designers: touch is “a secondary input mode”; keep layout and control size; enlarge tap targets, not controls; reveal hidden controls on a tap near them, hide them when the person is done or after a few seconds unused, with sidebars and a tab’s close button as the exceptions, and give the essential ones a context menu.4
FAQ
Is the AppKit migration article live?
Yes. “Adopting gesture recognizers in your app” returned 404 at noon PT on October 28 and was live by 9:51 p.m., listed in AppKit’s Essentials and Gestures sections and linked from the landing page’s documentation tile.15
Are exclusive views on in my app?
If your Info.plist has no NSViewGestureRecognizerIsExclusive key, yes: “If your app’s information property list doesn’t include the NSViewGestureRecognizerIsExclusive key, the system treats it as YES.”2 A project created from the macOS App template in Xcode 27.1 or the 27.2 beta with the AppKit app delegate lifecycle (every Storyboard or XIB project) gets the key as a build setting with the value NO; the same template with the default SwiftUI lifecycle writes no key (author analysis of the templates on this Mac).7
What happens to a gesture recognizer that never finishes?
It holds its exclusive view until the system cancels it: a recognizer “is stuck if it hasn’t reached a terminal state within 2 seconds after the person stops interacting with your app,” and the system “automatically transitions it to the NSGestureRecognizer.State.cancelled state.”2 Set the NSCrashOnStuckGestureTimeout default while debugging to get a stack trace instead.2
Why does my tracking loop stop getting drags after I add a pan recognizer?
Because the recognizer’s buttonMask is at its default and claims the primary mouse button: “A mouse drag then begins the gesture recognizer, and AppKit doesn’t call your mouseDown(with:) override, so the tracking loop doesn’t run.” Set buttonMask to 0 for a touch-only recognizer.1
How does a game receive touch on the Mac?
As emulated mouse events, in a documented order, with a mouseMoved(with:) to the new location before each mouseDown(with:), and as an additional GCMouse whose deltas are not continuous. Setting NSIsTouchNative to YES switches the game to gesture recognizers and removes the emulated GCMouse.3
Does the HIG give a touch target size for the Mac?
No. The Gestures page says “Make tap targets bigger, not the controls themselves” and defers to framework controls for the size; the 36-point figure is in the two talks, not the HIG.49
Where do the facts in this post come from?
Apple’s two AppKit articles, TN3216, the three HIG pages and the landing page, all fetched at 9:51 p.m. PT on October 28, 2026, with the article’s availability, the HIG pages and the landing page compared against checks at noon and 11:49 a.m. the same day; Apple’s documentation JSON for every symbol named; the June technote for the comparison; the Tech Talks’ subtitle tracks for the talk quotations; and the headers and project templates inside Xcode 27.1 and the Xcode 27.2 beta on my Mac, which is author analysis. Press coverage is not a source.
Sources
-
Apple Developer Documentation, Adopting gesture recognizers in your app, AppKit. Fetched October 28, 2026 at 9:51 p.m. PT (HTTP 200,
Last-Modified: Wed, 28 Oct 2026 21:30:45 GMT); the same URL and its JSON endpoint returned 404 at noon PT the same day. Listed in the AppKit framework page’s Essentials section, second after “Adopting Liquid Glass,” and under “Gesture handling” on Gestures. The Wayback Machine’s first capture is 2026-10-29 01:08:12 UTC (CDX row20261029010812, status 200; the raw header and CDX receipts are saved with the post’s evidence). Sections quoted: Overview, “Prepare your app for a Mac with a touch display” (the table, the four-line test, the search list), “Filter gesture recognizer candidates,” “Handle touch input,” “Update your event-handling code,” “Replace common mouse-down overrides,” “Handle tracking-loop compatibility,” “Replace your custom tracking loops with gesture recognizers,” “Add touch support to your custom tracking loop,” “Write a custom gesture recognizer,” “Account for gesture recognizer behavior changes,” and “Support non-AppKit UI frameworks.” The Swift listing in this post is a four-line reduction of the article’s “Add touch support to your custom tracking loop” listing. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple Developer Documentation, Handling AppKit gestures, AppKit. Fetched October 28, 2026 at 9:51 p.m. PT (
Last-Modified: Wed, 28 Oct 2026 05:35:41 GMT); no revision history on the page; not linked from TN3212; no capture in the Wayback Machine’s index as of that time. Sections quoted: Overview, “Track gesture recognizer states,” “Understand gesture recognizer gathering,” “Implement a custom gesture recognizer,” “Prioritize gesture recognizers,” “Delay responder events,” “Configure exclusive views,” “Replace event-tracking loops,” and “Diagnose unresponsive gesture recognizers.” ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple Developer Documentation, TN3216: Supporting touch input for games on macOS, revision history “2026-10-28 First published,” fetched October 28, 2026 at 9:51 p.m. PT (
Last-Modified: Wed, 28 Oct 2026 21:09:50 GMT). Sections quoted: Overview, “Extend the player experience meaningfully,” “Understand automatic touch support in existing games,” “Understand GCMouse emulation,” “Opt out of mouse emulation,” “Implement touch events,” “Route gestures in your game engine,” “Use standard AppKit controls for automatic touch support,” and “Review your touch integration.” ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple Developer, Human Interface Guidelines, fetched October 28, 2026 at 9:51 p.m. PT and compared with copies fetched at noon PT the same day: Designing for macOS, change log “October 28, 2026 | Added guidance for touch input.”; Gestures, change log “October 28, 2026 | Added guidance for gestures and touch input in macOS.”, with the macOS platform section and the table “macOS trackpad and touch display gestures”; Pointing devices, change log “October 28, 2026 | Added guidance for revealing hidden controls with touch on Mac, and updated guidance and artwork for iPadOS and macOS.”, with the section “Touch support for hidden controls”; Live Activities, byte-identical to the noon copy, change log ending October 13, 2026. The noon copies of the first three contain none of the quoted text. The server’s
Last-Modifiedheader on the new Gestures JSON readsTue, 27 Oct 2026 05:11:02 GMT, withX-Cache: hit-staleon the first request at 9:59 p.m. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple Developer, Get ready for Touch on Mac, fetched October 28, 2026 at 11:49 a.m. PT (three tiles reading “Coming soon,” links in HTML comments) and at 9:51 p.m. PT: “Xcode 27.3 beta” / “Download the latest SDKs to build, run, and test your app.” / “Coming soon”; “Documentation resources” / “Learn how to build and optimize your app for touch input.” linking the article and the sample; “Design resources” / “Create experiences that take advantage of touch user interface patterns.” linking Gestures, Designing for macOS, Pointing devices and Apple Design Resources, whose product-bezel list includes “MacBook Pro PROLED XDR Display”; the comment
TK skillpresent in both fetches. Latest News RSS, newest item “Say hello to Touch on Mac,” Wed, 28 Oct 2026 10:00:55 PDT; Releases RSS, newest items dated Mon, 26 Oct 2026 10:00:00 PDT (iOS, iPadOS, macOS 27.2 (26B123), tvOS, visionOS and watchOS 27.2); Tech Talks listing 111456, 111467 and 111468 and no other talk on touch or Live Activities; the page for Bring Touch to your Mac app, with no link to either article; all fetched at 9:51 p.m. PT. ↩↩↩↩↩↩↩↩↩ -
Apple documentation JSON (
developer.apple.com/tutorials/data/documentation/<path>.json), fetched October 28, 2026 at 9:51 p.m. PT. Found:trackingEndedOutside(macOS 11.0),NSEvent.isTouchSwipeNavigationEnabled(macOS 27.0),NSTextSelectionManager.gesturesForFailureRequirements(macOS 27.0),gestureRecognizer(_:shouldRequireFailureOf:)on NSGestureRecognizerDelegate (macOS 10.10),NSViewGestureRecognizerIsExclusive(“The default value isYES.”),NSIsTouchNative, Mouse, Keyboard, and Trackpad (the two sentences on the responder chain and touch), Supporting drag and drop through file promises (the sentence on direct touches), and SwiftUI updates (Last-Modified: Wed, 28 Oct 2026 21:58:06 GMT, newest section “September 2026”). Not found (HTTP 404):appkit/nswindow/disablesdirecttouchsystemgestures,uikit/uiviewcontroller/prefersdirecttouchsystemgesturesdisabled,swiftui/view/directtouchsystemgesturesdisabled(),activitykit/alertconfiguration/announcement(_:),xcode-release-notes/xcode-27_3-release-notes,macos-release-notes/macos-27_3-release-notes. ActivityKit:Last-Modified: Thu, 08 Oct 2026 15:31:35 GMT, platforms iOS, iPadOS and Mac Catalyst. ↩↩↩↩↩↩↩↩↩↩↩ -
SDK and template check on October 28, 2026 (author analysis), in
MacOSX27.0.sdkinside Xcode 27.1 (27A9275):NSControl.hline 180 readsNSControlEventTrackingEndedOutside = 1 << 7, with the same line inMacOSX27.2.sdkinside the Xcode 27.2 beta (27B5019j);NSEvent.hline 496 reads@property (class, readonly, getter=isTouchSwipeNavigationEnabled) BOOL touchSwipeNavigationEnabled API_AVAILABLE(macos(27));;NSTextSelectionManager.hline 59 declaresgesturesForFailureRequirements;NSGestureRecognizer.hlines 116 and 149 declareshouldRequireFailureOfGestureRecognizer:. Project templates, read with plistlib, in Xcode 27.1 (27A9275) and the Xcode 27.2 beta (27B5019j), underPlatforms/MacOSX.platform/Developer/Library/Xcode/Templates/Project Templates/macOS/Application/:Cocoa App Base.xctemplate/TemplateInfo.plistholds the keyINFOPLIST_KEY_NSViewGestureRecognizerIsExclusivewith the string valueNOatOptions[0] (appLifecycle) / Units / Cocoa / Targets[0] / SharedSettings, and nowhere else;macOS App Lifecycle.xctemplate, underProject Templates/macOS/Base/, defines theappLifecycleoption with noName,Typepopup,DefaultSwiftUIandValuesSwiftUI,Cocoa;macOS Application UI.xctemplatedefinesuserInterfacewithNameInterface:,DefaultSwiftUIandValuesSwiftUI,Storyboard,XIB, and itsStoryboardandXIBunits, like one of itsSwiftUIunits, carryRequiredOptionswithappLifecycleCocoa;App.xctemplatedescends fromCocoa App BasethroughCore Data App,macOS SwiftUI App,macOS Application UIandCocoa App Native Base;Cocoa Document App.xctemplatewrites anInfo.plist:NSViewGestureRecognizerIsExclusivenode whose definition is<key>NSViewGestureRecognizerIsExclusive</key><false/>, inside itsCocoalifecycle unit;Game.xctemplatesetsappLifecycletoOverrideCocoa;Document App.xctemplatedescends from bothCocoa Document AppandCocoa App Base(throughCore Data Document App,macOS SwiftUI Document-Based App,macOS Application UIandCocoa App Native Base). Only those two template files contain the key in either Xcode. Xcode’sINFOPLIST_KEY_build settings are written into the generated Info.plist. ↩↩↩↩↩↩↩↩↩↩ -
Apple Developer Documentation, TN3212: Adopting gesture recognizers for Sidecar touch support, revision history “2026-06-08 First published,” section “Support third-party UI frameworks,” four bullets: “A tap emulates a mouse-down followed immediately by a mouse-up,” “A touch and immediate pan emulates trackpad scroll events, even if the UI doesn’t respond to them,” “A long press without movement emulates a rightMouseDown(with:) (optionally followed by rightMouseDragged(with:)) with a rightMouseUp(with:) on touch lift,” and “A two-finger pinch or rotation simultaneously emulates both the trackpad magnify and rotate gestures.” Fetched October 28, 2026. ↩↩↩
-
Apple Developer, Tech Talks Bring Touch to your Mac app (13:19, Raleigh Ledet) and Design for touch on Mac (16:52, Cas Lemmens), October 28, 2026. Quotations are from the English subtitle tracks; the time in parentheses is the first cue of the quoted span: “drag with a small hold delay” (111468, 11:31), “there’s a gesture modernization skill that has an understanding of the features and changes I’ve talked about” (111468, 12:16), “Touch on macOS uses a touch target of 36 points” (111456, 9:17), “a minimum of 36 points on each axis” (111468, 5:53). The talk page’s code listing “Use NSControl events to detect tracking” (2:24) registers
addTarget(_:action:for:)for[.trackingBegan, .trackingEntered],[.trackingExited, .trackingEndedInside]and.trackingCancelled. ↩↩↩↩↩