Post

Replies

Boosts

Views

Activity

Resizing text to fit available space
My app displays some text that should appear the same regardless of the container view or window size, i.e. it should grow and shrink with the container view or window. On iOS there is UILabel.adjustsFontSizeToFitWidth but I couldn't find any equivalent API on macOS. On the internet some people suggest to iteratively set a smaller font size until the text fits the available space, but I thought there must be a more efficient solution. How does UILabel.adjustsFontSizeToFitWidth do it? My expectation was that setting a font's size to a fraction of the window width or height would do the trick, but when resizing the window I can see a slightly different portion of it. class ViewController: NSViewController { override func loadView() { view = MyView(frame: CGRect(x: 0, y: 0, width: 400, height: 400)) NSLayoutConstraint.activate([view.widthAnchor.constraint(equalTo: view.heightAnchor, multiplier: 3), view.heightAnchor.constraint(greaterThanOrEqualToConstant: 100)]) } } class MyView: NSView { let textField = NSTextField(labelWithString: String(repeating: "a b c d e f g h i j k l m n o p q r s t u v w x y z ", count: 2)) override init(frame frameRect: NSRect) { super.init(frame: frameRect) textField.translatesAutoresizingMaskIntoConstraints = false textField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) addSubview(textField) NSLayoutConstraint.activate([textField.topAnchor.constraint(equalTo: topAnchor), textField.leadingAnchor.constraint(equalTo: leadingAnchor), textField.trailingAnchor.constraint(equalTo: trailingAnchor)]) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func resize(withOldSuperviewSize oldSize: NSSize) { // textField.font = .systemFont(ofSize: frame.width * 0.05) textField.font = .systemFont(ofSize: frame.height * 0.1) } }
Topic: UI Frameworks SubTopic: AppKit Tags:
13
0
358
Nov ’25
Making filecopy faster by changing block size
I'm using the filecopy function to copy many files and I noticed that it always takes longer than similar tools like cp or a Finder copy (I already did a comparison in my other post). What I didn't know before was that I can set the block size which apparently can have a big influence on how fast the file copy operation is. The question now is: what should I consider before manually setting the block size? Does it make sense to have a block size that is not a power of 2? Can certain block sizes cause an error, such as a value that is too large (for the Mac the code is running on, or for the source and target devices)? When should or shouldn't I deviate from the default? Is there a way to find out the optimal block size for given source and target devices, or at least one that performs better than the default? In the following sample code I tried to measure the average time for varying block sizes, but I'm not sure it's the best way to measure it, since each loop iteration can have wildly different durations. class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ aNotification: Notification) { let openPanel = NSOpenPanel() openPanel.runModal() let source = openPanel.urls[0] openPanel.canChooseDirectories = true openPanel.canChooseFiles = false openPanel.runModal() let destination = openPanel.urls[0].appendingPathComponent(source.lastPathComponent) let date = Date() let count = 10 for _ in 0..<count { try? FileManager.default.removeItem(at: destination) do { try copy(source: source, destination: destination) } catch { preconditionFailure(error.localizedDescription) } } print(-date.timeIntervalSinceNow / Double(count)) } func copy(source: URL, destination: URL) throws { try source.withUnsafeFileSystemRepresentation { sourcePath in try destination.withUnsafeFileSystemRepresentation { destinationPath in let state = copyfile_state_alloc() defer { copyfile_state_free(state) } // var bsize = Int32(16_777_216) var bsize = Int32(1_048_576) if copyfile_state_set(state, UInt32(COPYFILE_STATE_BSIZE), &bsize) != 0 || copyfile_state_set(state, UInt32(COPYFILE_STATE_STATUS_CB), unsafeBitCast(copyfileCallback, to: UnsafeRawPointer.self)) != 0 || copyfile_state_set(state, UInt32(COPYFILE_STATE_STATUS_CTX), unsafeBitCast(self, to: UnsafeRawPointer.self)) != 0 || copyfile(sourcePath, destinationPath, state, copyfile_flags_t(COPYFILE_ALL | COPYFILE_NOFOLLOW | COPYFILE_EXCL)) != 0 { throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno)) } } } } private let copyfileCallback: copyfile_callback_t = { what, stage, state, src, dst, ctx in if what == COPYFILE_COPY_DATA { if stage == COPYFILE_ERR { return COPYFILE_QUIT } var size: off_t = 0 copyfile_state_get(state, UInt32(COPYFILE_STATE_COPIED), &size) let appDelegate = unsafeBitCast(ctx, to: AppDelegate.self) if !appDelegate.setCopyFileProgress(Int64(size)) { return COPYFILE_QUIT } } return COPYFILE_CONTINUE } private func setCopyFileProgress(_ progress: Int64) -> Bool { return true } }
14
1
1.5k
Jan ’24
Finder shows warning "Apple could not verify file is free of malware" when setting my app as "Always open with"
A user of my AppKit, document-based app brought to my attention that when setting it as the default app to open a certain file with extension .md (by choosing in the Finder "File > Open With > Other", then selecting my app and enabling "Always open with"), trying to open it with a double-click displays the warning "Apple could not verify [file] is free of malware that may harm your mac or compromise your privacy". This is what happens for me: When keeping the default app for a .md file (Xcode in my case), the file opens just fine. When choosing my app in the "File > Open With" menu, the file opens just fine in my app. But when setting my app as the default app (see above), the warning is displayed. From that moment on, choosing my app in the "File > Open With" menu doesn't work anymore. Selecting Xcode doesn't work either. Only setting Xcode again as the default app allows me to open it in Xcode, but my app still isn't allowed to open it. Is this a macOS issue, or can I do anything in my app to prevent it? Where should I start looking for the issue in my code?
14
0
375
Aug ’25
Weird crashes when accessing Swift Array
For some time now Xcode has been downloading crash reports from users of my app about crashes related to arrays. One of them looks like this: ... Code Type: ARM-64 Parent Process: launchd [1] User ID: 501 Date/Time: 2024-07-18 14:59:40.4375 +0800 OS Version: macOS 15.0 (24A5289h) ... Crashed Thread: 0 Exception Type: EXC_BREAKPOINT (SIGTRAP) Exception Codes: 0x0000000000000001, 0x00000001045048b8 Termination Reason: Namespace SIGNAL, Code 5 Trace/BPT trap: 5 Terminating Process: exc handler [1771] Thread 0 Crashed: 0 MyApp 0x00000001045048b8 specialized Collection.map<A>(_:) + 596 1 MyApp 0x00000001045011e4 MyViewController.validateToolbarButtons() + 648 (MyViewController.swift:742) ... The relevant code looks like this: class MyViewController { func validateToolbarButtons() { let indexes = tableView.clickedRow == -1 || tableView.selectedRowIndexes.contains(tableView.clickedRow) ? tableView.selectedRowIndexes : IndexSet(integer: tableView.clickedRow) let items = indexes.map({ myArray[$0] }) ... } } The second crash looks like this: ... Code Type: X86-64 (Native) Parent Process: launchd [1] User ID: 502 Date/Time: 2024-07-15 15:53:35.2229 -0400 OS Version: macOS 15.0 (24A5289h) ... Crashed Thread: 0 Exception Type: EXC_BAD_INSTRUCTION (SIGILL) Exception Codes: 0x0000000000000001, 0x0000000000000000 Termination Reason: Namespace SIGNAL, Code 4 Illegal instruction: 4 Terminating Process: exc handler [13244] Thread 0 Crashed: 0 libswiftCore.dylib 0x00007ff812904fc0 _assertionFailure(_:_:flags:) + 288 1 MyApp 0x0000000101a31e04 specialized _ArrayBuffer._getElementSlowPath(_:) + 516 2 MyApp 0x00000001019d04eb MyObject.myProperty.setter + 203 (MyObject.swift:706) 3 MyApp 0x000000010192f66e MyViewController.controlTextDidChange(_:) + 190 (MyViewController.swift:166) ... And the relevant code looks like this: class MyObject { var myProperty: [MyObject] { get { ... } set { let items = newValue.map({ $0.id }) ... } } } What could cause such crashes? Could they be caused by anything other than concurrent access from multiple threads (which I'm quite sure is not the case here, as I only access these arrays from the main thread)?
16
0
2.3k
Mar ’25
NSDocument doesn't autosave last changes
I had noticed an unsettling behaviour about NSDocument some years ago and created FB7392851, but the feedback didn't go forward, so I just updated it and hopefully here or there someone can explain what's going on. When running a simple document-based app with a text view, what I type before closing the app may be discarded without notice. To reproduce it, you can use the code below, then: Type "asdf" in the text view. Wait until the Xcode console logs "saving". You can trigger it by switching to another app and back again. Type something else in the text view, such as "asdf" on a new line. Quit the app. Relaunch the app. The second line has been discarded. Am I doing something wrong or is this a bug? Is there a workaround? class ViewController: NSViewController { @IBOutlet var textView: NSTextView! } class Document: NSDocument { private(set) var text = "" override class var autosavesInPlace: Bool { return true } override func makeWindowControllers() { let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil) let windowController = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("Document Window Controller")) as! NSWindowController (windowController.contentViewController as? ViewController)?.textView.string = text self.addWindowController(windowController) } override func data(ofType typeName: String) throws -> Data { Swift.print("saving") text = (windowControllers.first?.contentViewController as? ViewController)?.textView.string ?? "" return Data(text.utf8) } override func read(from data: Data, ofType typeName: String) throws { text = String(decoding: data, as: UTF8.self) (windowControllers.first?.contentViewController as? ViewController)?.textView.string = text } }
Topic: UI Frameworks SubTopic: AppKit Tags:
17
0
464
Oct ’25
getattrlistbulk lists same files over and over on macOS 15 Sequoia
A customer of mine reported that since updating to macOS 15 they aren't able to use my app anymore, which performs a deep scan of selected folders by recursively calling getattrlistbulk. The problem is that the app apparently keeps scanning forever, with the number of scanned files linearly increasing to infinity. This happens for some folders on a SMB volume. The customer confirmed that they can reproduce the issue with a small sample app that I attach below. At first, I created a sample app that only scans the contents of the selected folder without recursively scanning the subcontents, but the issue didn't happen anymore, so it seems to be related to recursively calling getattrlistbulk. The output of the sample app on the customer's Mac is similar to this: start scan /Volumes/shares/Backup/Documents level 0 fileManagerCount 2847 continue scan /Volumes/shares/Backup/Documents new items 8, sum 8, errno 34 /Volumes/shares/Backup/Documents/A.doc /Volumes/shares/Backup/Documents/B.doc ... continue scan /Volumes/shares/Backup/Documents new items 7, sum 1903, errno 0 /Volumes/shares/Backup/Documents/FKV.pdf /Volumes/shares/Backup/Documents/KFW.doc /Volumes/shares/Backup/Documents/A.doc /Volumes/shares/Backup/Documents/B.doc ... which shows that counting the number of files in the root folder by using try FileManager.default.contentsOfDirectory(atPath: path).count returns 2847, while getattrlistbulk lists about 1903 files and then starts listing the files from the beginning, not even between repeated calls, but within a single call. What could this issue be caused by? (The website won't let me attach .swift files, so I include the source code of the sample app as a text attachment.) ViewController.swift
18
0
1.2k
Dec ’24
Creating file bookmarks doesn't work anymore on macOS 15 Sequoia
Before updating to macOS 15 Sequoia, I used to be able to create file bookmarks with this code: let openPanel = NSOpenPanel() openPanel.runModal() let url = openPanel.urls[0] do { let _ = try url.bookmarkData(options: [.withSecurityScope]) } catch { print(error) } Now I get an error Error Domain=NSCocoaErrorDomain Code=256 "Failed to retrieve app-scope key" These are the entitlements: com.apple.security.app-sandbox com.apple.security.files.user-selected.read-write com.apple.security.files.bookmarks.app-scope Strangely, my own apps continued working, after updating to macOS 15 some days ago, until a few moments ago. Then it seems that all of a sudden my existing bookmarks couldn't be resolved anymore, and no new bookmarks could be created. What could be the problem?
28
3
3.4k
Feb ’25