Hello,
I have a .app NSApplication which is ran as a LaunchDaemon, in it's lifecycle I never call any AppKit functions (I start it with CFRunLoopRun). (mentioned on this post as well).
I intercept a couple of signals using signal(s) in order to trigger CFRunLoopStop(CFRunLoopGetCurrent()) to do some cleanup.
This LaunchDaemon has the purpose of providing VPN connectivity, as such I call connect functions that trigger the "Add VPN configuration" dialog (I can't provide extra details about this, as I integrate another SDK so I'm not sure what happens under the hood) and I noticed that whenever it is displayed, after allowing it, during the lifecycle of the application when it's time to send the signal, the signal isn't received.
I tried re-adding the NSApp callbacks in order to investigate, but it looks like those aren't called as well.
I'm interested in knowing more about this scenario and what happens... I couldn't really find information about this dialog...
Thanks!
AppKit
RSS for tagConstruct and manage a graphical, event-driven user interface for your macOS app using AppKit.
Posts under AppKit tag
200 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
That's a question for Mac app (Cocoa).
I want to change the standard highlighting.
I thought to use tableView.selectionHighlightStyle.
But there are only 2 values: .none and .regular. Cannot find how to define a custom one.
So I tried a workaround:
set tableView.selectionHighlightStyle to .none
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
tableView.selectionHighlightStyle = .none
keep track of previousSelection
Then, in tableViewSelectionDidChange
reset for previousSelection
func tableViewSelectionDidChange(_ notification: Notification) { }
if previousSelection >= 0 {
let cellView = theTableView.rowView(atRow: previousSelection, makeIfNecessary: false)
cellView?.layer?.backgroundColor = .clear
}
set for the selection to a custom color
let cellView = theTableView.rowView(atRow: row, makeIfNecessary: false)
cellView?.layer?.backgroundColor = CGColor(red: 0, green: 0, blue: 1, alpha: 0.4)
previousSelection = row
Result is disappointing :
Even though tableView.selectionHighlightStyle is set to .none, it does overlays the cellView?.layer
Is there a way to directly change the color for selection ?
I’m running macOS 15.7 with Xcode 26.2, and I’m trying to learn the basics of AppKit.
I’m fully aware that AppKit is considered a legacy / “old” technology and that Apple clearly does not promote it out of the box anymore. This is especially visible in recent versions of Xcode, where you can no longer create a macOS App template without Storyboards or SwiftUI.
That said, AppKit is still widely used under the hood, so I think it’s reasonable to at least understand its fundamentals.
Here’s the problem I’m facing.
I create a standard macOS App project using the Xcode template (AppKit App Delegate). Then I:
delete Main.storyboard
remove all storyboard references from Info.plist
try to create the window manually in applicationDidFinishLaunching
At this point, the project builds, but the app either:
does not show any window, or
behaves as if it were not a proper GUI app
While debugging, it looks like Xcode / Swift is treating my target more like a dylib / wrapper than a normal .app bundle (at least judging by runtime behavior and the lack of a proper AppKit lifecycle).
While searching for answers, I found this article:
https://ashidiqi.com/blog/how-to-setup-xcode-project-programmatically-with-appkit/
The author claims that in this scenario you must not use the @main attribute and instead need a main.swift file that manually calls NSApplicationMain.
However, the article also mentions that Xcode shows the error:
'main' attribute cannot be used in a module that contains top-level code
The confusing part is that I do not have any top-level code in my project when using @main.
At the same time, when I ask an LLM for clarification, it tells me that:
@main does not require a storyboard
removing a storyboard does not force you to switch to main.swift
@main should work fine for a fully programmatic AppKit app
So I’m left with two concrete questions:
What is the correct, modern answer here? When building a fully programmatic AppKit app (no storyboard, no SwiftUI), do you actually need main.swift, or is @main still fully supported and correct?
Is there any supported or recommended way in modern Xcode to get a “clean” AppKit app template without a storyboard? Either via project settings, templates, or some documented workflow — without having to fight the build system or end up with a half-broken target.
I’m not trying to fight Apple’s direction here — I just want to understand the correct way to work with AppKit in 2025, even if it’s not the preferred path anymore.
Thanks in advance for any clarification.
The documentation says:
The caching behavior of the NSURL and CFURL APIs differ. For NSURL, all cached values (not temporary values) are automatically removed after each pass through the run loop. You only need to call the removeCachedResourceValueForKey: method when you want to clear the cache within a single execution of the run loop. The CFURL functions, on the other hand, do not automatically clear cached resource values. The client has complete control over the cache lifetimes, and you must use CFURLClearResourcePropertyCacheForKey or CFURLClearResourcePropertyCache to clear cached resource values.
https://developer.apple.com/documentation/foundation/nsurl/removeallcachedresourcevalues()?language=objc
Is this really true? In my experience I've had to explicitly remove cached resource values via -removeAllCachedResourceValues or removeCachedResourceValueForKey: otherwise the URL contains stale values.
For example on a URL that no longer exists I attempted to read NSURLIsHiddenKey and the last value was already cached. Instead of getting a NSFileNoSuchFileError I get the old cache value unless explicitly call -removeCachedResourceValueForKey: first and I'm fairly certain the value was cached on a previous run loop churn.
I have a MacOS app which displays PDFs, and I want to create a New document from the Clipboard, if the clipboard contains valid graphical data.
My problem is that even if it doesn't, I still get a blank new document window. AppKit always creates a new document.
I've tried overriding the newDocument function; I've tried avoiding the built-in functions altogether.
Are there any general recommendations for going about this?
My app has a menu called "Document". On Tahoe, this appears as a lightbulb icon.
Is this deliberate? Is it possible to stop this? Or do I just have to use a different name if I want people to read words?
When I appendAttributedString to [textView textStorage] it does not appear on the scrollable TextView.
However when I NSLog the [textView textStorage] the Attributed string is outputted, and is therefore stored in the textView, see below.
Occurs every time I ask to see the AttributedString I send to the textView.
[textView textStorage] attributedString
I need to see the attributedString displayed on the ScrollableTextView, but I don't know why I cannot see it.
Running print operation on WKWebView I hit EXC_BREAKPOINT and there is all kinds of console spew that looks concerning:
ERROR: The NSPrintOperation view's frame was not initialized properly before knowsPageRange: returned. (WKPrintingView)
** CGContextClipToRect: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.**
WebContent[7743] networkd_settings_read_from_file Sandbox is preventing this process from reading networkd settings file at "/Library/Preferences/com.apple.networkd.plist", please add an exception.
CRASHSTRING: XPC_ERROR_CONNECTION_INVALID from launchservicesd
CRASHSTRING: rdar://problem/28724618 Process unable to create connection because the sandbox denied the right to lookup com.apple.coreservices.launchservicesd and so this process cannot talk to launchservicesd.
WebContent[7921] The sandbox in this process does not allow access to RunningBoard.
Safe to ignore all this?
Is there any way I can know that another app has gone full screen? Please note that this is not what NSWindowDidEnterFullScreenNotification does, that only works for my own windows.
As for why I need to know: Say you're playing a YouTube video full screen. The video fills up the main display, and if there's a second display, it goes black. Well, mostly. I have a utility app with small status windows that remain on top. I'd like to be polite and hide them in this scenario.
I want to print the content of a WKWebView. I've done some searching, and many people have struggled with this over the years. Some claimed success, but their solutions don't work for me. One person created images for each pages and printed that, but then if you were to print to PDF, you'd get a PDF containing images rather than text.
If I just call the printView(_:)) method of the view, I get blank pages.
With the following more elaborate code, I get a partial printout, 11 out of what should be about 13 pages.
let info = NSPrintInfo.shared
info.topMargin = 72.0;
info.bottomMargin = 72.0;
info.leftMargin = 72.0;
info.rightMargin = 72.0;
info.isVerticallyCentered = false;
info.isHorizontallyCentered = false;
info.horizontalPagination = .fit;
info.verticalPagination = .automatic;
let printOp = webView!.printOperation( with: info )
printOp.canSpawnSeparateThread = true
printOp.view?.frame = NSMakeRect( 0, 0,
info.paperSize.width, info.paperSize.height )
printOp.runModal(for: webView.window!, delegate: self,
didRun: nil, contextInfo: nil )
When I run the above under the debugger, I see console messages saying
CGContextClipToRect: invalid context 0x0.
Once the print dialog appears, if I touch (but not change) the selected printer, then the page count changes to the correct value.
I’m running into a problem with SwiftUI/AppKit event handling on macOS Tahoe 26.2.
I have a layered view setup:
Bottom: AppKit NSView (NSViewRepresentable)
Middle: SwiftUI view in an NSHostingView with drag/tap gestures
Top: Another SwiftUI view in an NSHostingView
On macOS 26.2, the middle NSHostingView no longer receives mouse or drag events when the top NSHostingView is present. Events pass through to the AppKit view below. Removing the top layer immediately restores interaction. Everything works correctly on macOS Sequoia.
I’ve posted a full reproducible example and detailed explanation on Stack Overflow, including a single-file demo:
Stack Overflow post:
https://stackoverflow.com/q/79862332
I also found a related older discussion here, but couldn’t get the suggested workaround to apply:
https://developer.apple.com/forums/thread/759081
Any guidance would be appreciated.
Thanks!
I created an application extension that uses IdentityLookup framework for SMS filtering. How can I deploy my product as a library? I tried wrapping it as a framework, but it was not successful.
Hello,
I'm trying to make a white-Label sort of thing for my app, that is: a script runs before the app launches, sets a certain LaunchAgent command that sets and environment variable, and based on that variable's value tha main app's icon changes to a certain logo (change only happens in the dock because changing the icon on disk breaks the signature)
When the app launches it takes a noticeable time until the dock icon changes to what I want, so I worked around that by setting the app's plist property to hide the dock icon and then when the app is launched I call an objc++ function to display the icon in the dock again (this time it displays as the new icon)
The showing happens through [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
The problem happens when I try to close the app, it returns back to the old logo before closing which is what I want to prevent. I tried hiding the app dock icon before closing but even the hiding itself changes the icon before hiding
The hiding happens through [NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
My goal is that the main app icon doesn't appear to the user through the dock, and that the icon that is only visible is the other one that changes during runtime
The reason for this is that I have an app that should be visible differently depending on an environment variable that I set using an installer app. The app is the same for all users with very minor UI adjustments depending on that env variable's value. So instead of creating different versions of the app I'd like to have just 1 version that adjusts differently depending on the env variable's value. Somehow this is the only step left to have a smooth experience
Feel free to ask more clarification questions I'd be happy to help
Thank you
I have (had) a view controller that does a bit of manual layout in a -viewDidLayout override.
This was pretty easy to manage - however since introducing NSGlassEffectView into the view hierarchy I sometimes am getting hit with "Unable to simultaneously satisfy constraints" and Appkit would break a constraint to 'recover.' It appears translatesAutoresizingMaskIntoConstraints is creating some really weird fixed width and height constraints. Here I wasn't doing any autolayout - just add the glass view and set its frame in -viewDidLayout.
At runtime since I do manual layout in -viewDidLayout the frames are fixed and there is no real "error" in my app in practice though I wanted to get rid of the constraint breaking warning being logged because I know Autolayout can be aggressive about 'correctness' who knows if they decide to throw and not catch in the future.
In my perfect world I would probably just prefer a view.doesManualLayout = YES here - the subviews are big containers no labels so localization is not an issue for me. Rather than playing with autoresizing masks to get better translated constraints I decided to set translatesAutoresizingMaskIntoConstraints to NO and make the constraints myself. Now I get hit with the following exception:
"The window has been marked as needing another Layout Window pass, but it has already had more Layout Window passes than there are views in the window"
So this happens because the view which now has constraints -- I adjusted the frame of it one point in -viewDidLayout. My question is - is not legal to make changes in -viewDidLayout - which seems like the AppKit version of -viewDidLayoutSubviews.
In UIKit I always thought it was fine to make changes in -viewDidLayoutSubviews to frames - even if constraints were used - this is a place where you could override things in complex layouts that cannot be easily described in constraints. But in AppKit if you touch certain frames in -viewDidLayout it can now cause this exception (also related: https://developer.apple.com/forums/thread/806471)
I will change the constant of one of the constraints to account for the 1 point adjustment but my question still stands - is it not legal to touch frames in -viewDidLayout when autolayout constraints are used on that subview? It is (or at least was if I remember correctly) permitted to change the layout in -viewDidLayoutSubviews in UIKit but AppKit seems to be more aggressive in its checking for layout correctness).
What about calling -sizeToFit on a control in viewDidLayout or some method that has side effect of invalidating layout in a non obvious way, is doing things like this now 'dangerous?'
Shouldn't AppKit just block the layout from being invalidated from within -viewDidLayout - and leave whatever the layout is as is when viewDidLayout returns (thus making -viewDidLayout a useful place to override layout in the rare cases where you need a sledgehammer?)
We have submitted a feedback for this issue: FB21230723
We're building a note-taking app for iOS and macOS that uses both UITextView and NSTextView.
When performing text input that involves a marked range (such as Japanese input) in a UITextView or NSTextView with a UITextViewDelegate or NSTextViewDelegate set, the text view's marked range (markedTextRange / markedRange()) has not yet been updated at the moment when shouldChangeTextIn is invoked.
UITextViewDelegate.textView(_:shouldChangeTextIn:replacementText:)
NSTextViewDelegate.textView(_:shouldChangeTextIn:replacementString:)
The current behavior is this when entering text in Japanese: (same for NSTextView)
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
print(textView.markedTextRange != nil) // prints out false
DispatchQueue.main.async {
print(textView.markedTextRange != nil) // prints out true
}
}
However, we need the value of markedTextRange right away in order to determine whether to return true or false from this method.
Is there any workaround for this issue?
Hello,
I'm currently working on an app that will have a different name (and UI) depending on an environment variable that is set by that app's custom installer that I also wrote.
I'm trying to change the app name that is displayed in the menu bar (top left) programmatically. I've done some research and found that it's probably not possible to do so because it's fetched from the app's Info.plist
The answer is probably already there but I'll ask the question: Is it possible to change the app's name in the menu bar without tampering with the Info.plist (as it will break the signature at runtime, and it can't be done at compile time)
The use case here is that I'm shipping a single app with multiple installers, because the app will be the same for all sets of users but it will have minor UI changes (which in my opinion, don't need an entire separate release), the installers will set a flag that the app will, based on, change its UI to the corresponding set of users. For the most part it's been successful but only small issue remain like this one.
The reason it's done like that is when updating I'll ship 1 release for all users to update rather than having to ship multiple releases with no difference between them
I received the following crash:
Thread 0 Crashed:
libsystem_kernel.dylib __pthread_kill + 8
libsystem_pthread.dylib pthread_kill + 296 (pthread.c:1721)
libsystem_c.dylib abort + 124 (abort.c:122)
libc++abi.dylib __abort_message + 132 (abort_message.cpp:66)
libc++abi.dylib demangling_terminate_handler() + 304 (cxa_default_handlers.cpp:76)
libobjc.A.dylib _objc_terminate() + 156 (objc-exception.mm:496)
libc++abi.dylib std::__terminate(void (*)()) + 16 (cxa_handlers.cpp:59)
libc++abi.dylib __cxxabiv1::failed_throw(__cxxabiv1::__cxa_exception*) + 88 (cxa_exception.cpp:152)
libc++abi.dylib __cxa_throw + 92 (cxa_exception.cpp:299)
libobjc.A.dylib objc_exception_throw + 448 (objc-exception.mm:385)
Foundation -[NSConcreteMutableAttributedString initWithString:] + 268 (NSAttributedString.m:1049)
CloudDocs -[BRCloudPathComponentDisplayMetadata initWithDisplayName:suffix:url:icon:] + 180 (BRCloudPathComponentDisplayMetadata.m:75)
CloudDocs -[NSURL(BRCloudPathComponent) br_pathComponentDisplayMetadataWithOptions:]_block_invoke + 516 (BRCloudPathComponentDisplayMetadata.m:292)
CloudDocs -[NSArray(BRAdditions) br_transform:keepNull:] + 228 (NSArray+BRAdditions.m:20)
CloudDocs -[NSURL(BRCloudPathComponent) br_pathComponentDisplayMetadataWithOptions:] + 76 (BRCloudPathComponentDisplayMetadata.m:276)
AppKit -[NSPathCell _autoUpdateCellContents] + 2080 (NSPathCell.m:442)
AppKit -[NSPathCell setURL:] + 76 (NSPathCell.m:599)
AppKit -[NSPathControl setURL:] + 64 (NSPathControl.m:366)
I tried reproducing on my end by passing various URLs in iCloud Drive to an NSPathControl, file reference urls, attempting to evict a URL from iCloud Drive then settings the URL property without luck.
Setting the URL to nil does not crash (the property is nullable). I have no idea how to trigger that code path. Anyone else run into this and have a workaround?
I am building a macOS utility using SwiftUI and Swift that records and displays keyboard shortcuts (like Cmd+C, Cmd+V) in the UI. To achieve this, I am using NSEvent.addGlobalMonitorForEvents(matching: [.keyDown]).
I am aware that global monitoring usually requires the app to be non-sandboxed. However, I am seeing some behavior I don't quite understand during development:
I started with a fresh SwiftUI project and disabled the App Sandbox.
I requested Accessibility permissions using AXIsProcessTrustedWithOptions, manually enabled it in System Settings, and the global monitor worked perfectly.
I then re-enabled the App Sandbox in "Signing & Capabilities."
To my surprise, the app still records global events from other applications, even though the Sandbox is now active.
Is this expected behavior? Does macOS "remember" the trust because the Bundle ID was previously authorized while non-sandboxed, or is there a specific reason a Sandboxed app can still use addGlobalMonitor if the user has manually granted Accessibility access?
My app's core feature is displaying these shortcuts for the user's own reference (productivity tracking). If the user is the one explicitly granting permission via the Accessibility privacy pane, will Apple still reject the app for using global event monitors within a Sandboxed environment?
Code snippet of my monitor:
// This is still firing even after re-enabling Sandbox
eventMonitor = NSEvent.addGlobalMonitorForEvents(matching: [.keyDown]) { event in
print("Captured: \(event.charactersIgnoringModifiers ?? "")")
}
I've tried cleaning the build folder and restarting the app, removing the app from accessibility permission, but the events keep coming through. I want to make sure I'm not relying on a "development glitch" before I commit to the App Store path.
Here is the full code anyone can use to try this :-
import SwiftUI
import Cocoa
import Combine
struct ShortcutEvent: Identifiable {
let id = UUID()
let displayString: String
let timestamp: Date
}
class KeyboardManager: ObservableObject {
@Published var isCapturing = false
@Published var capturedShortcuts: [ShortcutEvent] = []
private var eventMonitor: Any?
// 1. Check & Request Permissions
func checkAccessibilityPermissions() -> Bool {
let options: NSDictionary = [kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String: true]
let accessEnabled = AXIsProcessTrustedWithOptions(options)
return accessEnabled
}
// 2. Start Capture
func startCapture() {
guard checkAccessibilityPermissions() else {
print("Permission denied")
return
}
isCapturing = true
let mask: NSEvent.EventTypeMask = [.keyDown, .keyUp]
eventMonitor = NSEvent.addGlobalMonitorForEvents(matching: mask) { [weak self] event in
self?.processEvent(event)
}
}
// 3. Stop Capture
func stopCapture() {
if let monitor = eventMonitor {
NSEvent.removeMonitor(monitor)
eventMonitor = nil
}
isCapturing = false
}
private func processEvent(_ event: NSEvent) {
// Only log keyDown to avoid double-counting the UI display
guard event.type == .keyDown else { return }
var modifiers: [String] = []
var symbols: [String] = []
// Map symbols for the UI
if event.modifierFlags.contains(.command) {
modifiers.append("command")
symbols.append("⌘")
}
if event.modifierFlags.contains(.shift) {
modifiers.append("shift")
symbols.append("⇧")
}
if event.modifierFlags.contains(.option) {
modifiers.append("option")
symbols.append("⌥")
}
if event.modifierFlags.contains(.control) {
modifiers.append("control")
symbols.append("⌃")
}
let key = event.charactersIgnoringModifiers?.uppercased() ?? ""
// Only display if a modifier is active (to capture "shortcuts" vs regular typing)
if !symbols.isEmpty && !key.isEmpty {
let shortcutString = "\(symbols.joined(separator: " ")) + \(key)"
DispatchQueue.main.async {
// Insert at the top so the newest shortcut is visible
self.capturedShortcuts.insert(ShortcutEvent(displayString: shortcutString, timestamp: Date()), at: 0)
}
}
}
}
PS :- I just did another test by creating a fresh new project with the default App Sandbox enabled, and tried and there also it worked!!
Can I consider this a go to for MacOs app store than?
This example application crashes when entering any text to the token field with
FAULT: NSGenericException: The window has been marked as needing another Update Constraints in Window pass, but it has already had more Update Constraints in Window passes than there are views in the window.
The app uses controlTextDidChange to update a live preview where it accesses the objectValue of the token field.
If one character is entered, it also looks like the NSTokenFieldDelegate methods
tokenField(_:styleForRepresentedObject:) tokenField(_:editingStringForRepresentedObject:) tokenField(_:representedObjectForEditing:)
are called more than 10000 times until the example app crashes on macOS Tahoe 26 beta 6.
I've reported this issue with beta 1 as FB18088608, but haven't heard back so far.
I have multiple occurrences of this issue in my app, which is working fine on previous versions of macOS. I haven't found a workaround yet, and I’m getting anxious of this issue persisting into the official release.
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?