Post

Replies

Boosts

Views

Created

Does CLANG_CXX_LANGUAGE_STANDARD affect my Swift app and why is it not set to Compiler Default?
I was just comparing the build settings of two of my apps to try to understand why they behave differently (one of them uses the full screen on iPad, and the other one has small top and bottom black borders, although that's not the issue I want to discuss now). I saw that the option CLANG_CXX_LANGUAGE_STANDARD is set to gnu++0x for the older project, while it's set to gnu++17 for the newer one. The documentation lists different possible values and also a default one: Compiler Default: Tells the compiler to use its default C++ language dialect. This is normally the best choice unless you have specific needs. (Currently equivalent to GNU++98.) If it really is the best choice (normally), why is it not used when creating a new default Xcode project? Or is it better to select a newer compiler version (GNU++98 sounds quite old compared to GNU++17)? Also, does this affect Swift code?
2
0
440
Feb ’25
GKMatch.chooseBestHostingPlayer(_:) always returns nil player
I'm building a game with a client-server architecture. Using GKMatch.chooseBestHostingPlayer(_:) rarely works. When I started testing it today, it worked once at the very beginning, and since then it always succeeds on one client and returns nil on the other client. I'm testing with a Mac and an iPhone. Sometimes it fails on the Mac, sometimes on the iPhone. On the device that it succeeds on, the provided host can be the device itself or the other one. I created FB9583628 in August 2021, but after the Feedback Assistant team replied that they are not able to reproduce it, the feedback never went forward. import SceneKit import GameKit #if os(macOS) typealias ViewController = NSViewController #else typealias ViewController = UIViewController #endif class GameViewController: ViewController, GKMatchmakerViewControllerDelegate, GKMatchDelegate { var match: GKMatch? var matchStarted = false override func viewDidLoad() { super.viewDidLoad() GKLocalPlayer.local.authenticateHandler = authenticate } private func authenticate(_ viewController: ViewController?, _ error: Error?) { #if os(macOS) if let viewController = viewController { presentAsSheet(viewController) } else if let error = error { print(error) } else { print("authenticated as \(GKLocalPlayer.local.gamePlayerID)") let viewController = GKMatchmakerViewController(matchRequest: defaultMatchRequest())! viewController.matchmakerDelegate = self GKDialogController.shared().present(viewController) } #else if let viewController = viewController { present(viewController, animated: true) } else if let error = error { print(error) } else { print("authenticated as \(GKLocalPlayer.local.gamePlayerID)") let viewController = GKMatchmakerViewController(matchRequest: defaultMatchRequest())! viewController.matchmakerDelegate = self present(viewController, animated: true) } #endif } private func defaultMatchRequest() -> GKMatchRequest { let request = GKMatchRequest() request.minPlayers = 2 request.maxPlayers = 2 request.defaultNumberOfPlayers = 2 request.inviteMessage = "Ciao!" return request } func matchmakerViewControllerWasCancelled(_ viewController: GKMatchmakerViewController) { print("cancelled") } func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFailWithError error: Error) { print(error) } func matchmakerViewController(_ viewController: GKMatchmakerViewController, didFind match: GKMatch) { self.match = match match.delegate = self startMatch() } func match(_ match: GKMatch, player: GKPlayer, didChange state: GKPlayerConnectionState) { print("\(player.gamePlayerID) changed state to \(String(describing: state))") startMatch() } func startMatch() { let match = match! if matchStarted || match.expectedPlayerCount > 0 { return } print("starting match with local player \(GKLocalPlayer.local.gamePlayerID) and remote players \(match.players.map({ $0.gamePlayerID }))") match.chooseBestHostingPlayer { host in print("host is \(String(describing: host?.gamePlayerID))") } } }
4
0
403
Mar ’25
Crash reports downloaded by Xcode contain impossible call hierarchy
I was just having a look at some crash reports downloaded by Xcode, and I noticed the same wrong pattern I already mentioned here: the crash reports indicate that method A calls method B, which is impossible. In the first crash report below, method MainViewController.showSettings seems to be called by ConfirmMoveViewController.openSourceInFinder, which is impossible. ConfirmMoveViewController.openSourceInFinder is a context menu action in a modal window, and MainViewController.showSettings is in a completely different window and the two methods have no relation whatsoever. In the second crash report below, MainViewController.setSortMode is triggered by the press of a button (and nothing else) but seems to be called by OtherViewController.copy that can be triggered by a context menu (or keyboard shortcut). The two methods have no relation whatsoever. The rest of the stack trace confirm that it's indeed the button that was pressed. This seems to me like a quite serious bug in how macOS creates crash reports. 1.crash 2.crash
6
0
359
Mar ’25
NSTextView doesn't correctly redraw when deleting text and setting attribute at the same time
It seems that NSTextView has an issue with deleting text and setting any attribute at the same time, when it also has a textContainerInset. With the code below, after 1 second, the empty line in the text view is automatically deleted and the first line is colored red. The top part of the last line remains visible at its old position. Selecting the whole text and then deselecting it again makes the issue disappear. Is there a workaround? I've created FB16897003. class ViewController: NSViewController { @IBOutlet var textView: NSTextView! override func viewDidAppear() { textView.textContainerInset = CGSize(width: 0, height: 8) let _ = textView.layoutManager textView.textStorage!.setAttributedString(NSAttributedString(string: "1\n\n2\n3\n4")) textView.textStorage!.addAttribute(.foregroundColor, value: NSColor.labelColor, range: NSRange(location: 0, length: textView.textStorage!.length)) DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [self] in textView.selectedRange = NSRange(location: 3, length: 0) textView.deleteBackward(nil) textView.textStorage!.beginEditing() textView.textStorage!.addAttribute(.foregroundColor, value: NSColor.red, range: NSRange(location: 0, length: 2)) textView.textStorage!.endEditing() } } }
5
0
293
Mar ’25
Crash when assigning NSImage to `@objc dynamic var` property
Xcode downloaded a crash report for my app which I don't quite understand. It seems the following line caused the crash: myEntity.image = newImage where myEntity is of type MyEntity: class MyEntity: NSObject, Identifiable { @objc dynamic var image: NSImage! ... } The code is called on the main thread. According to the crash report, thread 0 makes that assignment, and at the same time thread 16 is calling [NSImageView asynchronousPreparation:prepareResultUsingParameters:]. What could cause such a crash? Could I be doing something wrong or is this a bug in macOS? crash.crash
11
0
244
Mar ’25
NSView.knowsPageRange(_:) called twice when showing print panel
When creating a default macOS document-based Xcode project and using the code below (and wiring the File menu's Print item to printDocument: instead of the default print:, which does nothing), opening the print panel causes PrintView.knowsPageRange(_:) to be called twice. Is this a bug? My app populates PrintView dynamically, and for large documents it can be quite inefficient to populate it once, only for the contents to be immediately discarded and populated again. A workaround that came to my mind would be to check if the print options have changed, though I'm not sure if it's a reliable indicator that the print preview is effectively the same. I created FB17018494. class Document: NSDocument { override func makeWindowControllers() { addWindowController(NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil).instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("Document Window Controller")) as! NSWindowController) } override func printOperation(withSettings printSettings: [NSPrintInfo.AttributeKey : Any]) throws -> NSPrintOperation { return NSPrintOperation(view: PrintView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)), printInfo: NSPrintInfo(dictionary: printSettings)) } } class PrintView: NSView { override func knowsPageRange(_ range: NSRangePointer) -> Bool { print("knowsPageRange") range.pointee = NSRange(location: 1, length: 1) return true } }
Topic: UI Frameworks SubTopic: AppKit
1
0
73
Mar ’25
Printing NSTextStorage over multiple UITextView produces weird results
I would like to print a NSTextStorage on multiple pages and add annotations to the side margins corresponding to certain text ranges. For example, for all occurrences of # at the start of a line, the side margin should show an automatically increasing number. My idea was to create a NSLayoutManager and dynamically add NSTextContainer instances to it until all text is laid out. The layoutManager would then allow me to get the bounding rectangle of the interesting text ranges so that I can draw the corresponding numbers at the same height inside the side margin. This approach works well on macOS, but I'm having some issues on iOS. When running the code below in an iPad Simulator, I would expect that the print preview shows 3 pages, the first with the numbers 0-1, the second with the numbers 2-3, and the last one with the number 4. Instead the first page shows the number 4, the second one the numbers 2-4, and the last one the numbers 0-4. It's as if the pages are inverted, and each page shows the text starting at the correct location but always ending at the end of the complete text (and not the range assigned to the relative textContainer). I've created FB17026419. class ViewController: UIViewController { override func viewDidAppear(_ animated: Bool) { let printController = UIPrintInteractionController.shared let printPageRenderer = PrintPageRenderer() printPageRenderer.pageSize = CGSize(width: 100, height: 100) printPageRenderer.textStorage = NSTextStorage(string: (0..<5).map({ "\($0)" }).joined(separator: "\n"), attributes: [.font: UIFont.systemFont(ofSize: 30)]) printController.printPageRenderer = printPageRenderer printController.present(animated: true) { _, _, error in if let error = error { print(error.localizedDescription) } } } } class PrintPageRenderer: UIPrintPageRenderer, NSLayoutManagerDelegate { var pageSize: CGSize! var textStorage: NSTextStorage! private let layoutManager = NSLayoutManager() private var textViews = [UITextView]() override var numberOfPages: Int { if !Thread.isMainThread { return DispatchQueue.main.sync { [self] in numberOfPages } } printFormatters = nil layoutManager.delegate = self textStorage.addLayoutManager(layoutManager) if textStorage.length > 0 { let glyphRange = layoutManager.glyphRange(forCharacterRange: NSRange(location: textStorage.length - 1, length: 0), actualCharacterRange: nil) layoutManager.textContainer(forGlyphAt: glyphRange.location, effectiveRange: nil) } var page = 0 for textView in textViews { let printFormatter = textView.viewPrintFormatter() addPrintFormatter(printFormatter, startingAtPageAt: page) page += printFormatter.pageCount } return page } func layoutManager(_ layoutManager: NSLayoutManager, didCompleteLayoutFor textContainer: NSTextContainer?, atEnd layoutFinishedFlag: Bool) { if textContainer == nil { addPage() } } private func addPage() { let textContainer = NSTextContainer(size: pageSize) layoutManager.addTextContainer(textContainer) let textView = UITextView(frame: CGRect(origin: .zero, size: pageSize), textContainer: textContainer) textViews.append(textView) } }
Topic: UI Frameworks SubTopic: UIKit Tags:
4
0
124
Mar ’25
Force NSDocument save panel to select most specific type in format popup button
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?
Topic: UI Frameworks SubTopic: AppKit Tags:
6
1
163
Apr ’25
NSTextView.shouldDrawInsertionPoint doesn't work with TextKit 2
The following code only ever causes shouldDrawInsertionPoint to be printed (no drawInsertionPoint), but even if that method returns false, the blinking insertion point is still drawn. On the other hand, with TextKit 1 it works as expected. Is there a way to hide the default insertion point in TextKit 2? My app draws its own. I've filed FB13684251. class TextView: NSTextView { override var shouldDrawInsertionPoint: Bool { print("shouldDrawInsertionPoint") return false } override func drawInsertionPoint(in rect: NSRect, color: NSColor, turnedOn flag: Bool) { print("drawInsertionPoint", flag) } } ``
Topic: UI Frameworks SubTopic: AppKit Tags:
9
0
193
Apr ’25
Get NSTextView selection frame with NSTextLayoutManager
I'm trying to update my app to use TextKit 2. The one thing that I'm still not sure about is how I can get the selection frame. My app uses it to auto-scroll the text to keep the cursor at the same height when the text wraps onto a new line or a newline is manually inserted. Currently I'm using NSLayoutManager.layoutManager!.boundingRect(forGlyphRange:in:). The code below almost works. When editing the text or changing the selection, the current selection frame is printed out. My expectation is that the selection frame after a text or selection change should be equal to the selection frame before the next text change. I've noticed that this is not always true when the text has a NSParagraphStyle with spacing > 0. As long as I type at the end of the text, everything's fine, but if I insert some lines, then move the selection somewhere into the middle of the text and insert another newline, the frame printed after manually moving the selection is different than the frame before the newline is inserted. It seems that the offset between the two frames is exactly the same as the paragraph style's spacing. Instead when moving the selection with the arrow key the printed frames are correct. I've filed FB17104954. class ViewController: NSViewController, NSTextViewDelegate { private var textView: NSTextView! override func loadView() { let scrollView = NSScrollView(frame: CGRect(x: 0, y: 0, width: 400, height: 400)) textView = NSTextView(frame: scrollView.frame) textView.autoresizingMask = [.width, .height] textView.delegate = self let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = 40 textView.typingAttributes = [.foregroundColor: NSColor.labelColor, .paragraphStyle: paragraphStyle] scrollView.documentView = textView scrollView.hasVerticalScroller = true view = scrollView } func textView(_ textView: NSTextView, shouldChangeTextIn affectedCharRange: NSRange, replacementString: String?) -> Bool { print("before", selectionFrame.maxY, selectionFrame) return true } func textDidChange(_ notification: Notification) { print("after ", selectionFrame.maxY, selectionFrame) } func textViewDidChangeSelection(_ notification: Notification) { print("select", selectionFrame.maxY, selectionFrame) } var selectionFrame: CGRect { guard let selection = textView.textLayoutManager!.textSelections.first?.textRanges.first else { return .null } var frame = CGRect.null textView.textLayoutManager!.ensureLayout(for: selection) textView.textLayoutManager!.enumerateTextSegments(in: selection, type: .selection, options: [.rangeNotRequired]) { _, rect, _, _ in frame = rect return false } return frame } }
0
1
128
Apr ’25
NSToolbar doesn't restore displayMode when NSWindow.titleVisibility = .hidden
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 } }
Topic: UI Frameworks SubTopic: AppKit Tags:
2
0
84
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
100
Apr ’25