Construct and manage a graphical, event-driven user interface for your macOS app using AppKit.

Posts under AppKit tag

193 Posts

Post

Replies

Boosts

Views

Activity

How to hide the tab bar in SwiftUI's TabView for macOS?
In SwiftUI for macOS, how can I hide the tab bar when using TabView? I would like to provide my own tab bar implementation. In AppKit's NSTabViewController, we can do the following: let tabViewController = NSTabViewController() tabViewController.tabStyle = .unspecified I've come across various posts that suggest using the .toolbar modifier, but none appear to work on macOS (or at least I haven't found the right implementation). struct ContentView: View { var body: some View { TabView { // ... content } <- which view modifier hides the tab bar? } } Latest macOS, Latest Xcode
3
0
212
May ’25
Action Extension Won't Launch Outside Mac App Store: Prompting policy for hardened runtime; service: kTCCServiceAppleEvents requires entitlement com.apple.security.automation.apple-events but it is missing
I have an outside Mac App Store app. It has an action extension. I can't get it to run from Xcode. I try to debug it from Safari. It shows up in the menu when I click the 'rollover' button but it doesn't show up in the UI at all. Xcode doesn't give me any indication as to what the problem is. I see this logs out in console when I try to open the action extension: Prompting policy for hardened runtime; service: kTCCServiceAppleEvents requires entitlement com.apple.security.automation.apple-events but it is missing for accessing={TCCDProcess: identifier=BundleIdForActionExtHere, pid=6650, auid=501, euid=501, binary_path=/Applications/AppNamehere.app/Contents/PlugIns/ActionExtension.appex/Contents/MacOS/ActionExtension}, requesting={TCCDProcess: identifier=com.apple.appleeventsd, pid=550, auid=55, euid=55, binary_path=/System/Library/CoreServices/appleeventsd}, I don't see why the Action extension needs Apple events but I added it to the entitlements anyway but it doesn't seem to matter. The action extension fails to open.
1
0
90
May ’25
How to effectively use task(id:) when multiple properties are involved?
While adopting SwiftUI (and Swift Concurrency) into a macOS/AppKit application, I'm making extensive use of the .task(id:) view modifier. In general, this is working better than expected however I'm curious if there are design patterns I can better leverage when the number of properties that need to be "monitored" grows. Consider the following pseudo-view whereby I want to call updateFilters whenever one of three separate strings is changed. struct FiltersView: View { @State var argument1: String @State var argument2: String @State var argument3: String var body: some View { TextField($argument1) TextField($argument2) TextField($argument3) }.task(id: argument1) { await updateFilters() }.task(id: argument2) { await updateFilters() }.task(id: argument3) { await updateFilters() } } Is there a better way to handle this? The best I've come up with is to nest the properties inside struct. While that works, I now find myself creating these "dummy types" in a bunch of views whenever two or more properties need to trigger an update. ex: struct FiltersView: View { struct Components: Equatable { var argument1: String var argument2: String var argument3: String } @State var components: Components var body: some View { // TextField's with bindings to $components... }.task(id: components) { await updateFilters() } } Curious if there are any cleaner ways to accomplish this because this gets a bit annoying over a lot of views and gets cumbersome when some values are passed down to child views. It also adds an entire layer of indirection who's only purpose is to trigger task(id:).
2
0
127
May ’25
App crashed when click the selected content on HTML with custom font-family
Crash Stack: thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=1, subcode=0x19ba3bb04) frame #0: 0x000000019ba3bb04 CoreFoundation`forwarding.cold.2 + 92 frame #1: 0x000000019b8ab718 CoreFoundation`forwarding + 1288 frame #2: 0x000000019b8ab150 CoreFoundation`_CF_forwarding_prep_0 + 96 frame #3: 0x000000019df230b0 CoreText`TCFRef<CTRun*>::Retain(void const*) + 40 frame #4: 0x000000019e052050 CoreText`CreateFontWithFontURL(__CFURL const*, __CFString const*, __CFString const*) + 476 frame #5: 0x000000019e052874 CoreText`TCGFontCache::CopyFont(__CFURL const*, __CFString const*, __CFString const*) + 144 frame #6: 0x000000019df27dcc CoreText`TBaseFont::CopyNativeFont() const + 232 frame #7: 0x000000019df8ee64 CoreText`TBaseFont::GetInitializedGraphicsFont() const + 152 frame #8: 0x000000019df26d70 CoreText`TBaseFont::CopyVariationAxes() const + 296 frame #9: 0x000000019df2d148 CoreText`TDescriptor::InitBaseFont(unsigned long, double) + 768 frame #10: 0x000000019df21358 CoreText`TDescriptor::CreateMatchingDescriptor(__CFSet const*, double, unsigned long) const + 604 frame #11: 0x000000019df251f8 CoreText`CTFontCreateWithFontDescriptor + 68 frame #12: 0x00000001bff8dfb8 WebCore`WebCore::createCTFont(__CFDictionary const*, float, unsigned int, __CFString const*, __CFString const*) + 124 frame #13: 0x00000001bff8e8bc WebCore`WebCore::FontPlatformData::fromIPCData(float, WebCore::FontOrientation&&, WebCore::FontWidthVariant&&, WebCore::TextRenderingMode&&, bool, bool, std::__1::variant<WebCore::FontPlatformSerializedData, WebCore::FontPlatformSerializedCreationData>&&) + 228 frame #14: 0x00000001c128eef4 WebKit`IPC::ArgumentCoder<WebCore::Font, void>::decode(IPC::Decoder&) + 1352 frame #15: 0x00000001c1333ca4 WebKit`std::__1::optional<WTF::HashMap<WTF::String, WebCore::AttributedString::AttributeValue, WTF::DefaultHashWTF::String, WTF::HashTraitsWTF::String, WTF::HashTraitsWebCore::AttributedString::AttributeValue, WTF::HashTableTraits>> IPC::ArgumentCoder<WTF::HashMap<WTF::String, WebCore::AttributedString::AttributeValue, WTF::DefaultHashWTF::String, WTF::HashTraitsWTF::String, WTF::HashTraitsWebCore::AttributedString::AttributeValue, WTF::HashTableTraits>, void>::decodeIPC::Decoder(IPC::Decoder&) + 480 frame #16: 0x00000001c1333a5c WebKit`std::__1::optional<WTF::HashMap<WTF::String, WebCore::AttributedString::AttributeValue, WTF::DefaultHashWTF::String, WTF::HashTraitsWTF::String, WTF::HashTraitsWebCore::AttributedString::AttributeValue, WTF::HashTableTraits>> IPC::Decoder::decode<WTF::HashMap<WTF::String, WebCore::AttributedString::AttributeValue, WTF::DefaultHashWTF::String, WTF::HashTraitsWTF::String, WTF::HashTraitsWebCore::AttributedString::AttributeValue, WTF::HashTableTraits>>() + 28 frame #17: 0x00000001c1333804 WebKit`std::__1::optional<std::__1::pair<WebCore::AttributedString::Range, WTF::HashMap<WTF::String, WebCore::AttributedString::AttributeValue, WTF::DefaultHashWTF::String, WTF::HashTraitsWTF::String, WTF::HashTraitsWebCore::AttributedString::AttributeValue, WTF::HashTableTraits>>> IPC::Decoder::decode<std::__1::pair<WebCore::AttributedString::Range, WTF::HashMap<WTF::String, WebCore::AttributedString::AttributeValue, WTF::DefaultHashWTF::String, WTF::HashTraitsWTF::String, WTF::HashTraitsWebCore::AttributedString::AttributeValue, WTF::HashTableTraits>>>() + 156 frame #18: 0x00000001c121f368 WebKit`IPC::ArgumentCoder<WebCore::AttributedString, void>::decode(IPC::Decoder&) + 172 frame #19: 0x00000001c121f124 WebKit`std::__1::optionalWebCore::AttributedString IPC::Decoder::decodeWebCore::AttributedString() + 28 frame #20: 0x00000001c12594ec WebKit`IPC::ArgumentCoder<WebCore::DictionaryPopupInfo, void>::decode(IPC::Decoder&) + 76 frame #21: 0x00000001c12d0660 WebKit`std::__1::optionalWebCore::DictionaryPopupInfo IPC::Decoder::decodeWebCore::DictionaryPopupInfo() + 28 frame #22: 0x00000001c12ceef0 WebKit`IPC::ArgumentCoder<WebKit::WebHitTestResultData, void>::decode(IPC::Decoder&) + 1292 frame #23: 0x00000001c1338950 WebKit`std::__1::optionalWebKit::WebHitTestResultData IPC::Decoder::decodeWebKit::WebHitTestResultData() + 28 frame #24: 0x00000001c1ec7edc WebKit`WebKit::WebPageProxy::didReceiveMessage(IPC::Connection&, IPC::Decoder&) + 31392 frame #25: 0x00000001c1fb8f28 WebKit`IPC::MessageReceiverMap::dispatchMessage(IPC::Connection&, IPC::Decoder&) + 272 frame #26: 0x00000001c19ab2c0 WebKit`WebKit::WebProcessProxy::didReceiveMessage(IPC::Connection&, IPC::Decoder&) + 44 frame #27: 0x00000001c1fb3254 WebKit`IPC::Connection::dispatchMessage(WTF::UniqueRefIPC::Decoder) + 252 frame #28: 0x00000001c1fb3768 WebKit`IPC::Connection::dispatchIncomingMessages() + 576 frame #29: 0x00000001b9ab90c4 JavaScriptCore`WTF::RunLoop::performWork() + 204 frame #30: 0x00000001b9ab9fec JavaScriptCore`WTF::RunLoop::performWork(void*) + 36 frame #31: 0x000000019b8cc8a4 CoreFoundation`CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION + 28 frame #32: 0x000000019b8cc838 CoreFoundation`__CFRunLoopDoSource0 + 176 frame #33: 0x000000019b8cc59c CoreFoundation`__CFRunLoopDoSources0 + 244 frame #34: 0x000000019b8cb138 CoreFoundation`__CFRunLoopRun + 840 frame #35: 0x000000019b8ca734 CoreFoundation`CFRunLoopRunSpecific + 588 frame #36: 0x00000001a6e39530 HIToolbox`RunCurrentEventLoopInMode + 292 frame #37: 0x00000001a6e3f348 HIToolbox`ReceiveNextEventCommon + 676 frame #38: 0x00000001a6e3f508 HIToolbox`_BlockUntilNextEventMatchingListInModeWithFilter + 76 frame #39: 0x000000019f442848 AppKit`_DPSNextEvent + 660 frame #40: 0x000000019fda8c24 AppKit`-[NSApplication(NSEventRouting) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 688 frame #41: 0x000000019f435874 AppKit`-[NSApplication run] + 480 frame #42: 0x000000019f40c068 AppKit`NSApplicationMain + 888 frame #43: 0x00000001ca56a70c SwiftUI`merged generic specialization <SwiftUI.TestingAppDelegate> of function signature specialization <Arg[0] = Existential To Protocol Constrained Generic> of SwiftUI.runApp(__C.NSResponder & __C.NSApplicationDelegate) -> Swift.Never + 160 frame #44: 0x00000001ca9e09a0 SwiftUI`SwiftUI.runApp<τ_0_0 where τ_0_0: SwiftUI.App>(τ_0_0) -> Swift.Never + 140 frame #45: 0x00000001cad5ce68 SwiftUI`static SwiftUI.App.main() -> () + 224 frame #46: 0x0000000105943104 MyApp Dev.debug.dylib`static MyMacApp.$main() at :0 frame #47: 0x0000000105943c9c MyApp Dev.debug.dylib`main at MyMacApp.swift:24:8 frame #48: 0x000000019b464274 dyld`start + 2840
1
0
168
May ’25
Can SwiftUI TextFields in a List on macOS be marked as always editable?
In SwiftUI's List, on macOS, if I embed a TextField then the text field is presented as non-editable. If the user clicks on the text and waits a short period of time, the text field will become editable. I'm aware this is generally the correct behaviour for macOS. However, is there a way in SwiftUI to supress this behaviour such that the TextField is always presented as being editable? I want a scrollable, List of editable text fields, much like how a Form is presented. The reason I'm not using a Form is because I want List's support for reordering by drag-and-drop (.onMove). Use Case A view that allows a user to compose a questionnaire. They are able to add and remove questions (rows) and each question is editable. They require drag-and-drop support so that they can reorder the questions.
0
0
120
May ’25
How do you restore a Sheet's window frame in SwiftUI for macOS
On macOS, it's not uncommon to present windows as sheets that can be resized. By setting the NSWindow's various frame auto save properties, you can restore the size of the sheet the next time it is presented. When presenting a Sheet from within SwiftUI using the .sheet view modifier, how can I preserve and restore the sheet's frame size? The closest I've been able to come is to put the SwiftUI view into a custom NSHostingController and then into an NSViewControllerRepresentable and then override viewWillAppear and look for self.view.window, which is all little awkward. Is there a more idiomatic way to achieve this in "pure" SwiftUI?
2
0
100
May ’25
-applicationDockMenu: method on NSApplicationDelegate doesn't work when attached to debugger
When I add a simple menu to the dock via the NSApplicationDelegate method -applicationDockMenu: and run the app from Xcode it doesn't work. -(NSMenu*)applicationDockMenu:(NSApplication*)sender { NSMenu *dockMenu = [self buildDockMenu]; if (dockMenu != nil) { NSLog(@"Returning dock menu."); return dockMenu; } else { NSLog(@"Not ready to build dock menu"); return nil; } } When I run the app, my main app window shows up but nothing logs out in -applicationDockMenu: until I click outside my app's window (so if I click the desktop background, or a Finder window, or whatever). Then after I click outside my app's main window this logs out: Returning dock menu. The "Not ready to build dock menu" message does not log out. But...when I right click on the dock icon, the menu doesn't show up. But if I stop the app from Xcode and just run it not attached to the debugger, the dock menu does show up. But this makes the debugging/testing situation not ideal.
3
0
146
May ’25
How to correctly set a Picker's selection and contents in SwiftUI for macOS?
How do you atomically set a Picker's selection and contents on macOS such that you don't end up in a situation where the selection is not present within the Picker's content? I presume Picker on macOS is implemented as an NSPopUpButton and an NSPopUpButton doesn't really like the concept of "no selection". SwiftUI, when presented with that, outputs: Picker: the selection "nil" is invalid and does not have an associated tag, this will give undefined results. Consider the following pseudo code: struct ParentView: View { @State private var items: [Item] var body: some View { ChildView(items: items) } } struct ChildView: View { let items: [Item] @State private var selectedItem: Item? var body: some View { Picker("", selection: $selectedItem) { ForEach(items) { item in Text(item.name).tag(item) } } } } When items gets passed down from ParentView to the ChildView, it's entirely possible that the current value in selectedItem represents an Item that is not longer in the items[] array. You can "catch" that by using .onAppear, .task, .onChange and maybe some other modifiers, but not until after at least one render pass has happened and an error has likely been reported because selectedItem is nil or it's not represented in the items[] array. Because selectedItem is private state, a value can't easily be passed down from the parent view, though even if it could that just kind of moves the problem one level higher up. What is the correct way to handle this type of data flow in SwiftUI for macOS?
1
0
167
May ’25
How to reopen a closed SwiftUI WindowGroup window programmatically without user interaction?
I’m building a macOS app using SwiftUI with a WindowGroup(id: "rootWindow") for the main UI. The app shows a countdown timer, and the timer continues to run even after the user closes the main window (clicks the red "X"). When the timer reaches 0, I want to automatically reopen that window and bring the app to the front. I’m currently using the following code to bring the app to the foreground and show the window when the app is still open (but not focused/resign active state): NSApp.activate(ignoringOtherApps: true) NSApp.windows.forEach { window in if window.identifier?.rawValue.starts(with: "rootWindow") { window.makeKeyAndOrderFront(nil) } } However, this doesn’t work when the window has been closed. At that point, NSApp.windows no longer contains my SwiftUI window, and I have no reference to recreate or reopen it. I also cannot use openWindow environment value as it requires a view. How can I programmatically reopen a SwiftUI WindowGroup window after it’s been closed, without requiring any user interaction (like clicking the Dock icon)?
2
0
108
May ’25
Is it reasonable to vend an NSView from a "ViewModel" when using NSViewRepresentable instead of implementing the Coordinator pattern?
I'm currently integrating SwiftUI into an AppKit based application and was curious if the design pattern below was viable or not. In order to "bridge" between AppKit and SwiftUI, most of my SwiftUI "root" views have aViewModel that is accessible to the SwiftUI view via @ObservedObject. When a SwiftUI views need to use NSViewRepresentable I'm finding the use of a ViewModel and a Coordinator to be an unnecessary layer of indirection. In cases where it makes sense, I've just used the ViewModel as the Coordinator and it all appears to be working ok, but I'm curious if this is reasonable design pattern or if I'm overlooking something. Consider the following pseudo code: // 1. A normal @ObservedObject acting as the ViewModel that also owns and manages an NSTableView. @MainActor final class ViewModel: ObservedObject, NSTableView... { let scrollView: NSScrollView let tableView: NSTableView @Published var selectedTitle: String init() { // ViewModel manages tableView as its dataSource and delegate. tableView.dataSource = self tableView.delegate = self } func reload() { tableView.reloadData() } // Update view model properties. // Simpler than passing back up through a Coordinator. func tableViewSelectionDidChange(_ notification: Notification) { selectedTitle = tableView.selectedItem.title } } // 2. A normal SwiftUI view, mostly driven by the ViewModel. struct ContentView: View { @ObservedObject model: ViewModel var body: some View { Text(model.selectedTitle) // No need to pass anything down other than the view model. MyTableView(model: model) Button("Reload") { model.reload() } Button("Delete") { model.deleteRow(...) } } } // 3. A barebones NSViewRepresentable that just vends the required NSView. No other state is required as the ViewModel handles all interactions with the view. struct MyTableView: NSViewRepresentable { // Can this even be an NSView? let model: ViewModel func makeNSView(context: Context) -> some NSView { return model.scrollView } func updateNSView(_ nsView: NSViewType, context: Context) { // Not needed, all updates are driven through the ViewModel. } } From what I can tell, the above is working as expected, but I'm curious if there are some situations where this could "break", particularly around the lifecycle of NSViewRepresentable Would love to know if overall pattern is "ok" from a SwiftUI perspective.
0
0
58
Apr ’25
The @Environment(\.dismiss) value in SwiftUI for macOS does not dismiss a sheet presented by an NSWindowController.
I'm wondering what the correct, or recommended, way is to dismiss a SwiftUI that is being presented as a sheet hosted by an NSHostingController. The usual technique of invoking @Environment(\.dismiss) does not appear to work. Consider the code below. An NSWindowController is attempting to display a SwiftUI SettingsView as a sheet. The sheet is correctly presented, but the SettingsView is unable to dismiss itself. I am able to make it work by passing a closure into SettingsView that calls back to the NSWindowController but it's rather convoluted because SettingsView doesn't know the view controller that's hosting it until after SettingsView has been created, which means "finding" that view controller in the window controller to dismiss is more involved than it should be. Is there a better strategy to leverage here? final class MyViewController: NSViewController { @IBAction func buttonClicked(_ sender: NSButton) { if let presenter = window?.contentViewController { presenter.presentAsSheet(NSHostingController(rootView: SettingsView())) } } } struct SettingsView: View { @Environment(\.dismiss) private var dismiss var body: some View { VStack { Button("Cancel", role: .cancel) { dismiss() // This call does not dismiss the sheet. } .keyboardShortcut(.cancelAction) } } } Thank you. macOS 15.4.1 (24E263), Xcode 16.3 (16E140)
0
0
81
Apr ’25
Overriding NSDocument.prepareSavePanel(_:) hides file format popup button
I would like to provide a default filename when saving a document depending on the document data. I thought I could do so by overriding NSDocument.prepareSavePanel(_:) and setting NSSavePanel.nameFieldStringValue, but simply implementing that method seems to hide the file format popup button shown by default (see image). Calling super doesn't help. Is it possible to set a default filename and keep the file format popup button? On macOS 15, I can toggle NSSavePanel.showsContentTypes, but how about macOS 14 and older?
Topic: UI Frameworks SubTopic: AppKit Tags:
4
0
89
Apr ’25
NSTableView.clickedRow sometimes is greater than number of rows
Xcode has been downloading many similar crash reports for my app for some time now, related to an index out of range runtime exception when accessing a Swift array. The crashes always happen in methods triggered by user input or during menu item validation when I try to access the data source array by using the following code to determine the indexes of the relevant table rows: let indexes = clickedRow == -1 || selectedRowIndexes.contains(clickedRow) ? selectedRowIndexes : IndexSet(integer: clickedRow) I was never able to reproduce the crash until today. When the app crashed in the Xcode debugger, I examined the variables clickedRow and selectedRowIndexes.first, which were 1 and 0 respectively. What's interesting: the table view only contained one row, so clickedRow was effectively invalid. I tried to reproduce the issue several times afterwards, but it never happened again. What could cause this issue? What are the circumstances where it is invalid? Do I always have to explicitly check if clickedRow is within the data source range?
Topic: UI Frameworks SubTopic: AppKit Tags:
4
0
80
Apr ’25
How to match Status/Menu Bar font (Date and Time)
Hello, I am trying to match the font and the position of the date and time displayed. This is what it looks like: .font: NSFont.systemFont(ofSize: NSFont.systemFontSize, weight: .regular), .foregroundColor: NSColor.labelColor, .baselineOffset: 0.5 It looks great on built-in display, but on external 4K it is incorrect. The baselineOffest is unnecessary, and the font looks skinny in comparison. Can anyone comment on such issue?
5
0
149
Apr ’25
Does MKLookAroundViewController work on macOS?
Trying to incorporate a LookAroundView into my macOS application but unable to make the LookAroundView interactive at all. I can get it to display a static image, but there's no interactivity at all and no controls visible. This is using the SwiftUI LookAroundPreview view as well as trying to wrap MKLookAroundViewController inside an NSViewRepresentable. The navigation properties are set to true but that doesn't seem to make a difference. Would love to incorporate this feature but without interactivity its value is limited. macOS 15.4.1 (24E263), Xcode Version 16.3 (16E140)
1
0
100
Apr ’25
How to create a momentary segmented control in SwiftUI for macOS?
In AppKit, NSSegmentedControl has various styles defined by NSSegmentStyle and various tracking modes defined by NSSegmentSwitchTracking. How can we set these properties in SwiftUI? I'm currently using a Picker with the view modifier .pickerStyle(.segmented) applied but this seems to produce a segmented control with tracking set to "select one". In particular I'm looking for momentary tracking so that I can create navigation-style buttons for backward/forward navigation. Under AppKit, the canonical way to do this is an NSSegmentedControl of style separated and tracking momentary. Is that possible under SwiftUI for macOS? (Using the latest versions of everything.)
1
0
109
Apr ’25
VoiceOver incorrect focus on modal alert
When my macOS Cocoa app displays a modal alert with beginSheetModal(for:completionHandler:), VoiceOver sometimes seems to focus on an "illegal" upper level, where any attempts at navigation will give the unhelpful response "Alert, dialog", until you "drill down" with VO + shift + down or switch apps. After that, things will work as expected. Is this a known bug? Does it happen to anybody else, or am I doing something wrong?
3
1
112
Apr ’25
Should i set window.isReleasedWhenClosed to true or leave it to default?
Hi, In mac os swift ui application when i set window.isReleasedWhenClosed and when i close the window the app is getting crashed with exc_bad_access. but when i leave it to default value the app is not crashing. for some windows setting window.isReleasedWhenClosed to true is woking properly when closing the windows. But for some windows it is crashing. If i dont set it to true the window is not removed from NSApplication.shared.windows sometimes. I am confused about setting isReleasedWhenClosed to true Could someone calrify on this please. thank in advance.
2
0
67
Apr ’25