Apparently when setting a window to hide its title, the toolbar's displayMode is not restored when relaunching the app. For example, by default my app sets to show toolbar icons only, but when right-clicking it, selecting "Icon and Text" and relaunching the app, it's again "Icon Only".
Is there a workaround? I've filed FB17144212.
class ViewController: NSViewController, NSToolbarDelegate {
override func viewDidAppear() {
let toolbar = NSToolbar(identifier: "toolbar")
toolbar.delegate = self
toolbar.autosavesConfiguration = true
toolbar.displayMode = .iconOnly
view.window?.titleVisibility = .hidden
view.window?.toolbar = toolbar
view.window?.toolbarStyle = .unified
}
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [.init(rawValue: "item")]
}
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [.init(rawValue: "item")]
}
func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
let item = NSToolbarItem(itemIdentifier: itemIdentifier)
item.image = NSImage(named: NSImage.addTemplateName)!
item.label = "item"
return item
}
}
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I successfully installed macOS Tahoe 26 in June on a separate external partition. Now I want to update to beta 5, but when the Mac restarts to install the update, it shows the progress indicator for a minute or two, then boots again into the main partition which has macOS 15.6. Am I required to install it from scratch?
A user of my app reported that when trying to remove a file it always fails with the error "file couldn't be removed because you don't have permission to access it (Cocoa Error Domain 513)". After some testing, we found out that it's caused by trying to delete non-empty directories.
I'm using FileManager.removeItem(atPath:) which has worked fine for many years, but it seems that with their particular NAS, it doesn't work.
I could work around this by checking if the file is a directory, and if it is, enumerating the directory and remove each contained file before removing the directory itself. But shouldn't this already be taken care of? In the source code of FileManager I see that for Darwin platforms it calls
removefile(pathPtr, state, removefile_flags_t(REMOVEFILE_RECURSIVE))
so it seems that it should already work. Is the REMOVEFILE_RECURSIVE flag perhaps ignored by the device? But then, is the misleading "you don't have permission to access the file" error thrown by the device or by macOS?
For the FileManager source code, see https://github.com/swiftlang/swift-foundation/blob/1d5d70997410fc8b7700c8648b10d6fc28194202/Sources/FoundationEssentials/FileManager/FileOperations.swift#L444
The online documentation for fs_snapshot_create, which is on a website which apparently I'm not allowed to link to on this forum, mentions that some entitlement is necessary, but doesn't specify which one. Searching online I found someone mentioning com.apple.developer.vfs.snapshot, but when adding this to my entitlement file and building my Xcode project, I get the error
Provisioning profile "Mac Team Provisioning Profile: com.example.myApp" doesn't include the com.apple.developer.vfs.snapshot entitlement.
Searching some more online, I found someone mentioning that one has to request this entitlement from DTS. Is this true? I couldn't find any official documentation.
I actually want to make a snapshot of a user-selected directory so that my app can sync it to another volume while avoiding that the user makes changes during the sync process that would make the copy inconsistent. Would fs_snapshot_create be faster than traversing the chosen directory and creating clones of each nested file with filecopy and the flag COPYFILE_CLONE? Although I have the impression that only fs_snapshot_create could make a truly consistent snapshot.
When I connect to another Mac via Finder (using SMB), creating a hard link with FileManager.linkItem(atPath:toPath:) fails (both source and destination are on the remote Mac). I read online that SMB itself supports creating hard links, so is this a macOS limitation or bug?
Until now I was using FileManager.contentsEqual(atPath:andPath:) to compare file contents in my App Store app, but then a user reported that this operation is way slower than just copying the files (which I made faster a while ago, as explained in Making filecopy faster by changing block size).
I thought that maybe the FileManager implementation reads the two files with a small block size, so I implemented a custom comparison with the same block size I use for filecopy (as explained in the linked post), and it runs much faster. When using the code for testing repeatedly also found on that other post, this new implementation is about the same speed as FileManager for 1KB files, but runs 10-20x faster for 1MB files or bigger.
Feel free to comment on my implementation below.
extension FileManager {
func fastContentsEqual(atPath path1: String, andPath path2: String, progress: (_ delta: Int) -> Bool) -> Bool {
do {
let bufferSize = 16_777_216
let sourceDescriptor = open(path1, O_RDONLY | O_NOFOLLOW, 0)
if sourceDescriptor < 0 {
throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno))
}
let sourceFile = FileHandle(fileDescriptor: sourceDescriptor)
let destinationDescriptor = open(path2, O_RDONLY | O_NOFOLLOW, 0)
if destinationDescriptor < 0 {
throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno))
}
let destinationFile = FileHandle(fileDescriptor: destinationDescriptor)
var equal = true
while autoreleasepool(invoking: {
let sourceData = sourceFile.readData(ofLength: bufferSize)
let destinationData = destinationFile.readData(ofLength: bufferSize)
equal = sourceData == destinationData
return sourceData.count > 0 && progress(sourceData.count) && equal
}) { }
if close(sourceDescriptor) < 0 {
throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno))
}
if close(destinationDescriptor) < 0 {
throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno))
}
return equal
} catch {
return contentsEqual(atPath: path1, andPath: path2) // use this as a fallback for unsupported files (like symbolic links)
}
}
}
My app supports different plain text file formats, including the standard .txt and Markdown. When creating a new document, my app already asks which format it should have, so when saving it, I would expect that the save panel already selects that format in the popup button, but currently it always selects "Plain Text". For example, I would expect for a Markdown document that it selects "Markdown" instead of "Plain Text".
Is there a way to force it to select the most specific format matching the document format?
For many years I've had the following code to access the active objects of a table view in my App Store app:
class MyViewController: NSViewController: NSMenuItemValidation {
private var tableView: NSTableView!
private var objects = [MyObject]()
func numberOfRows(in tableView: NSTableView) -> Int {
return objects.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
// make view for row
}
private var activeObjects: [MyObject] {
return tableView?.activeRowIndexes.map({ objects[$0] }) ?? []
}
func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
let activeObjects = self.activeObjects
...
}
}
extension NSTableView {
var activeRowIndexes: IndexSet {
return clickedRow == -1 || selectedRowIndexes.contains(clickedRow) ? selectedRowIndexes : IndexSet(integer: clickedRow)
}
}
In one of the recent updates, I wanted to add some kind of header to the table view, so I decided to add a row at the beginning and offset the indexes by 1.
func numberOfRows(in tableView: NSTableView) -> Int {
return objects.count + 1
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
if row == 0 {
// make header view
} else {
// make view for row - 1
}
}
private var activeObjects: [MyObject] {
return tableView?.activeRowIndexes.subtracting(IndexSet(integer: 0)).map({ objects[$0 - 1] }) ?? []
}
But since I added this change, Xcode regularly downloads crash reports from clients crashing during menu item validation in IndexSet.map with reason Code 5 Trace/BPT trap: 5. I assumed that I was accessing an invalid array index, so I added some debug code: the crash report would then show the invalid index beside the crashed thread's name.
private var activeObjects: [MyObject] {
return tableView?.activeRowIndexes.subtracting(IndexSet(integer: 0)).map({ i in
if !objects.indices.contains(i - 1) {
Thread.current.name = (Thread.current.name ?? "") + ". Invalid index \(i - 1) for count \(objects.count)"
preconditionFailure()
}
return objects[i - 1]
}) ?? []
}
But the crash reports for this new app version look just like the old ones and the thread name is not changed. Indeed, when recreating an invalid index access on my Mac, the crash report mentions Array._checkSubscript(_:wasNativeTypeChecked:), which does not appear in the crash reports downloaded by Xcode.
Manually symbolicating the crash report also doesn't give any more information: all lines referring to my app code are resolved to either /<compiler-generated>:0 or MyViewController.swift:0.
Apparently the problem is not an invalid array index, but something else. Does anybody have a clue what the problem could be?
(Note: the crash report mentions Sequence.compactMap because now I'm effectively calling tableView?.activeRowIndexes.compactMap, but the same crash happened before when calling IndexSet.map, which would appear in the crash report as Collection.map.)
crash2.crash
In my app the user can select a source folder to be synced with a destination folder. The sync can also happen in response to a change in the source folder detected with FSEventStreamCreate.
If the user unzips an archive in the source folder and the sync process begins before the unzip operation has completed, the sync can fail because of a "Permission denied" error. I assume this is related to the posix permissions of the extracted folder being 420 during the unzip operation and (in my case) 511 afterwards.
Is there a way to detect than an unzip operation is in progress and wait until it has completed? I thought that using NSFileCoordinator would solve this issue, but unfortunately it's not the case. Since an unzip operation can last any amount of time, it's not ideal to just delay a sync by a fixed number of seconds and let the user deal with any error if the unzip operation takes longer.
let openPanel = NSOpenPanel()
openPanel.canChooseDirectories = true
if openPanel.runModal() == .cancel {
return
}
let url = openPanel.urls[0].appendingPathComponent("extracted", isDirectory: false)
var error: NSError?
NSFileCoordinator(filePresenter: nil).coordinate(readingItemAt: url, error: &error) { url in
do {
print(try FileManager.default.attributesOfItem(atPath: url.path).sorted(by: { $0.key.rawValue < $1.key.rawValue }).map({ ($0.key.rawValue, $0.value) }))
try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil)
} catch {
print(error)
}
}
if let error = error {
print("file coordinator error:", error)
}
In my app I have a background task performed on a custom DispatchQueue. When it has completed, I update the UI in DispatchQueue.main.async. In a particular case, the app then needs to show a modal window that contains a table view, but I have noticed that when scrolling through the tableview, it only responds very slowly.
It appears that this happens when the table view in the modal window is presented in DispatchQueue.main.async. Presenting it in perform(_:with:afterDelay:) or in a Timer.scheduledTimer(withTimeInterval:repeats:block:) on the other hand works. Why? This seems like an ugly workaround.
I created FB7448414 in November 2019 but got no response.
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
let windowController = NSWindowController(window: NSWindow(contentViewController: ViewController()))
// 1. works
// runModal(for: windowController)
// 2. works
// perform(#selector(runModal), with: windowController, afterDelay: 0)
// 3. works
// Timer.scheduledTimer(withTimeInterval: 0, repeats: false) { [self] _ in
// self.runModal(for: windowController)
// }
// 4. doesn't work
DispatchQueue.main.async {
self.runModal(for: windowController)
}
}
@objc func runModal(for windowController: NSWindowController) {
NSApp.runModal(for: windowController.window!)
}
}
class ViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {
override func loadView() {
let tableView = NSTableView()
tableView.dataSource = self
tableView.delegate = self
tableView.addTableColumn(NSTableColumn())
let scrollView = NSScrollView(frame: CGRect(x: 0, y: 0, width: 400, height: 400))
scrollView.documentView = tableView
scrollView.hasVerticalScroller = true
scrollView.translatesAutoresizingMaskIntoConstraints = false
view = scrollView
}
func numberOfRows(in tableView: NSTableView) -> Int {
return 100
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let view = NSTableCellView()
let textField = NSTextField(labelWithString: "\(row)")
textField.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(textField)
NSLayoutConstraint.activate([textField.leadingAnchor.constraint(equalTo: view.leadingAnchor), textField.trailingAnchor.constraint(equalTo: view.trailingAnchor), textField.topAnchor.constraint(equalTo: view.topAnchor), textField.bottomAnchor.constraint(equalTo: view.bottomAnchor)])
return view
}
}
I noticed that sometimes TextKit2 decides to crop some text instead of soft-wrapping it to the next line.
This can be reproduced by running the code below, then resizing the window by dragging the right margin to the right until you see the text with green background (starting with “file0”) at the end of the first line.
If you now slowly move the window margin back to the left, you’ll see that for some time that green “file0” text is cropped and so is the end of the text with red background, until at some point it is soft-wrapped on the second line.
I just created FB18289242. Is there a workaround?
class ViewController: NSViewController {
override func loadView() {
let textView = NSTextView(frame: CGRect(x: 0, y: 0, width: 400, height: 400))
let string = NSMutableAttributedString(string: "file0\t143548282\t1970-01-01T00:00:00Z\t1\t1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75", attributes: [.foregroundColor: NSColor.labelColor, .backgroundColor: NSColor.red.withAlphaComponent(0.2)])
string.append(NSAttributedString(string: "file0\t143548290\t1970-01-01T00:05:00Z\t 2\t0f6460d0ed7825fed6bda0f4d9c14942d88edc7ff236479212e69f081815e6f1742c272753b77cc6437f06ef93a46271c6ff9513c68945075212434080e60c82", attributes: [.foregroundColor: NSColor.labelColor, .backgroundColor: NSColor.green.withAlphaComponent(0.2)]))
textView.textContentStorage!.textStorage!.setAttributedString(string)
textView.autoresizingMask = [.width, .height]
let scrollView = NSScrollView(frame: CGRect(x: 0, y: 0, width: 400, height: 400))
scrollView.documentView = textView
scrollView.hasVerticalScroller = true
scrollView.translatesAutoresizingMaskIntoConstraints = false
view = scrollView
}
}
In 2020 I created FB7719215, which I updated several times (including just now) and in 2021 I created FB9204092, but the issue is still there: when I keep Xcode open (currently version 16.3), my battery drains much quicker, even when it's apparently idle. For instance, today I barely did anything in Xcode, but still it has been at a constant 90% CPU for the last hours, and I keep checking the battery percentage to check how much time I have left.
Does anyone at Apple has an explanation, workaround and/or fix?
I'm trying to copy some files from the Finder on macOS 14 to several Simulator instances running iOS 16 and 17. When I right-click the file in the Finder, I can select Share > Simulator, then a share dialog pops up where I select the relevant Simulator and click on Send. According to this official help topic, the Files app should open allowing me to choose the destination, but instead nothing happens and the shared file is nothing to be seen. What's the issue?
When I launch the Quick Look Preview Extension target in Xcode, an app called Quick Look Simulator opens with an almost empty window:
Online I read that the Terminal command qlmanage allows to test Quick Look plugins (which I think were an older format for creating Quick Look extensions), but running
qlmanage -p /path/to/previewed/file -c public.text -g /path/to/QuickLookPreviewExtension.appex
(where QuickLookPreviewExtension.appex is generated by the Xcode build and is located in the DerivedData folder) gives an error
Can't get generator at QuickLookPreviewExtension.appex
How can I debug a Quick Look Preview Extension?
All the threads only contain system calls. The crashed thread only contains a single call to my app's code which is main.swift:13.
What could cause such a crash?
crash.crash