Post

Replies

Boosts

Views

Activity

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
352
Apr ’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
110
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
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
90
Apr ’25
NSTableView is unresponsive when inside a modal window shown in DispatchQueue.main.async
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 } }
4
0
153
Jun ’25
Selecting ~/Library in open panel doesn't give access to ~/Library/Mail
A user of my app brought to my attention that unless they select their ~/Library/Mail folder explicitly in an open panel, they get an error when scanning it inside my app. I can confirm that I also get a permission error when trying to scan it as a subfolder of ~/Library, but not if I select it directly. I'm assuming this is intentional, but it would be nice to have an explanation or some documentation that I can point my users to when they encounter what appears to them as a bug in my app. What makes this matter even more confusing is that selecting a folder in any open panel of an app gives the app access to it for the lifetime of the app, but after restarting the app, access is lost again (unless it has a bookmark to it). This was probably the reason why the user thought that it worked in another app but not in mine. This is the code I use to scan: let openPanel = NSOpenPanel() openPanel.canChooseDirectories = true if openPanel.runModal() == .cancel { return } let enumerator = FileManager.default.enumerator(at: openPanel.urls[0], includingPropertiesForKeys: nil) { url, error in print(url.path, error) return true } while let url = enumerator?.nextObject() as? URL { } And this the error related to the Mail folder: ~/Library/Mail Error Domain=NSCocoaErrorDomain Code=257 "The file “Mail” couldn’t be opened because you don’t have permission to view it." UserInfo={NSURL=file:///~/Library/Mail, NSFilePath=/~/Library/Mail, NSUnderlyingError=0x600002991470 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
4
0
122
Sep ’25
NEFilterDataProvider.handleNewFlow(_:) gets called with same flow ids multiple times
Since NEFilterFlow.identifier is documented as The unique identifier of the flow., I thought I could use it to store the flow by its identifier in a dictionary in order to retrieve it later. I do this when the system extension pauses a flow because it needs to ask the user whether the flow should eventually be allowed or dropped. But then I noticed that sometimes when allowing a previously paused flow, identified by its identifier, my system extension doesn't find that flow anymore. After some debugging it turned out that this happens because I stored at least one other flow with the same id which, when confirmed, is removed again from the dictionary, so there is no more flow with that identifier waiting in the dictionary. Is it expected that the identifiers are recycled for different flows, or does it mean that the same flow is effectively being passed to handleNewFlow(_:) multiple times, such as if the extension waited "too long" between pausing a flow and allowing or dropping it? handle(_:) can be called multiple times for the same flow, but why .handleNewFlow(_:)? All flows with duplicate ids seem to be UDP, and the local host and port and remote host and port are the same for all flows with the same id. Most of the duplicate flows have a process path of /usr/sbin/mDNSResponder (resolved with the sourceAppAuditToken).
5
0
688
Dec ’23
NSAttributedString.enumerateAttribute(_:in) crashes with custom attribute in macOS Sequoia
The following code crashes on macOS 15 Sequoia: import Foundation let key = NSAttributedString.Key("org.example.key") let value = Value() let string = NSMutableAttributedString() string.append(NSAttributedString(string: "a", attributes: [:])) string.append(NSAttributedString(string: "b", attributes: [key: value])) string.append(NSAttributedString(string: "c", attributes: [:])) string.enumerateAttribute(key, in: NSRange(location: 0, length: string.length)) { value, range, stop in print(range) } class Value: Equatable, Hashable { static func == (lhs: Value, rhs: Value) -> Bool { return lhs === rhs } func hash(into hasher: inout Hasher) { hasher.combine(ObjectIdentifier(self)) } } The error is EXC_BAD_ACCESS (code=1, address=0x0) I wanted to run it on my external macOS 14 partition to confirm that it didn't crash before updating to macOS 15, but for some reason macOS will just restart and boot again into macOS 15. So I tried with macOS 13, which I was allowed to start for some reason, and I was able to confirm that the code doesn't crash. Is this a known issue, and is there a workaround? Removing the two lines that add the letters a and c, or just declaring class Value without conformance to Equatable, Hashable, interestingly, solves the issue.
5
0
642
Oct ’24
Take correctly sized screenshots with ScreenCaptureKit
I've been using CGWindowListCreateImage which automatically creates an image with the size of the captured window. But SCScreenshotManager.captureImage(contentFilter:configuration:) always creates images with the width and height specified in the provided SCStreamConfiguration. I could be setting the size explicitly by reading SCWindow.frame or SCContentFilter.contentRect and multiplying the width and height by SCContentFilter.pointPixelScale , but it won't work if I want to keep the window shadow with SCStreamConfiguration.ignoreShadowsSingleWindow = false. Is there a way and what's the best way to take full-resolution screenshots of the correct size? import Cocoa import ScreenCaptureKit class ViewController: NSViewController { @IBOutlet weak var imageView: NSImageView! override func viewDidAppear() { imageView.imageScaling = .scaleProportionallyUpOrDown view.wantsLayer = true view.layer!.backgroundColor = .init(red: 1, green: 0, blue: 0, alpha: 1) Task { let windows = try await SCShareableContent.excludingDesktopWindows(false, onScreenWindowsOnly: true).windows let window = windows[0] let filter = SCContentFilter(desktopIndependentWindow: window) let configuration = SCStreamConfiguration() configuration.ignoreShadowsSingleWindow = false configuration.showsCursor = false configuration.width = Int(Float(filter.contentRect.width) * filter.pointPixelScale) configuration.height = Int(Float(filter.contentRect.height) * filter.pointPixelScale) print(filter.contentRect) let windowImage = try await SCScreenshotManager.captureImage(contentFilter: filter, configuration: configuration) imageView.image = NSImage(cgImage: windowImage, size: CGSize(width: windowImage.width, height: windowImage.height)) } } }
5
0
916
Oct ’25
SKTexture used for SceneKit object is rendered too bright
I would like to preload and use some images for both SpriteKit and SceneKit models (my game uses SceneKit with a SpriteKit overlay), and as far as I can see the only efficient way would be to create and preload SKTexture objects which can be supplied to SKSpriteNode(texture:) and SCNMaterial.diffuse.contents. The problem is that SKTexture are rendered too bright in SceneKit, for some unknown reason. Here a comparison between rendering an image (from URL) and a SKTexture: And the code that produces it: let url = Bundle.main.url(forResource: "art.scnassets/texture.png", withExtension: nil)! let plane1 = SCNPlane(width: 10, height: 10) plane1.firstMaterial!.diffuse.contents = url.path let node1 = SCNNode(geometry: plane1) node1.position.x = -5 scene.rootNode.addChildNode(node1) let plane2 = SCNPlane(width: 10, height: 10) plane2.firstMaterial!.diffuse.contents = SKTexture(image: NSImage(byReferencing: url)) let node2 = SCNNode(geometry: plane2) node2.position.x = 5 scene.rootNode.addChildNode(node2) This issue was already mentioned in this other post, but since I wasn't notified of the reply from Quinn asking about the feedback number I created at the time, it didn't make any progress.
5
0
953
Nov ’24
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
257
Apr ’25
QLPreviewPanel takes forever to load content preview in macOS 26
After upgrading to macOS 26, I noticed that showing a Quicklook preview in my app is very slow. Showing small text files is fine, but some other files I've tried, such as a Numbers document, take about 30 seconds (during which the indeterminate loading indicator appears) before the preview is shown. When showing the preview of an app, such as Xcode, the panel opens immediately with a placeholder image for the Xcode icon, and the actual Xcode icon is shown only after about 25 seconds. During this time many logs appear: FPItemsFromURLsWithTimeout timed out (5.000000s) for: file:///.file/id=6571367.2/ (/) FPItemsFromURLsWithTimeout timed out (5.000000s) for: file:///.file/id=6571367.23684/ (/Users) FPItemsFromURLsWithTimeout timed out (5.000000s) for: file:///.file/id=6571367.248032/ (/Users/n{9}k) FPItemsFromURLsWithTimeout timed out (5.000000s) for: file:///.file/id=6571367.248084/ (/Users/n{9}k/Downloads) Failed to add registration dmf.policy.monitor.app with error: Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service named com.apple.dmd.policy was invalidated: failed at lookup with error 159 - Sandbox restriction." UserInfo={NSDebugDescription=The connection to service named com.apple.dmd.policy was invalidated: failed at lookup with error 159 - Sandbox restriction.} Failed to register application policy monitor with identifier 69DDBDB4-0736-42FA-BA7A-C8D7EA049E29 for types {( applicationcategories, websites, categories, applications )} with error: Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service named com.apple.dmd.policy was invalidated: failed at lookup with error 159 - Sandbox restriction." UserInfo={NSDebugDescription=The connection to service named com.apple.dmd.policy was invalidated: failed at lookup with error 159 - Sandbox restriction.} FPItemsFromURLsWithTimeout timed out (5.000000s) for: file:///.file/id=6571367.155797561/ (~/Downloads/X{3}e.app) It seems that Quicklook tries to access each parent directory of the previewed file, and each one fails after 5 seconds. Why is Quicklook all of a sudden so slow? It used to be almost instant in macOS 15. I created FB20268201. import Cocoa import Quartz @main class AppDelegate: NSObject, NSApplicationDelegate, QLPreviewPanelDataSource, QLPreviewPanelDelegate { var url: URL? func applicationDidFinishLaunching(_ notification: Notification) { let openPanel = NSOpenPanel() openPanel.runModal() url = openPanel.urls[0] QLPreviewPanel.shared()!.makeKeyAndOrderFront(nil) } override func acceptsPreviewPanelControl(_ panel: QLPreviewPanel!) -> Bool { return true } override func beginPreviewPanelControl(_ panel: QLPreviewPanel!) { panel.dataSource = self panel.delegate = self } override func endPreviewPanelControl(_ panel: QLPreviewPanel!) { panel.dataSource = nil panel.delegate = nil } func numberOfPreviewItems(in panel: QLPreviewPanel!) -> Int { return 1 } func previewPanel(_ panel: QLPreviewPanel!, previewItemAt index: Int) -> QLPreviewItem! { return url as? QLPreviewItem } }
5
1
274
1w
Xcode shows alert about unknown com.apple.quicklook.preview extension point when running on Apple Vision Pro Simulator
I have an iOS app with a QuickLook extension. I also added Apple Vision Pro in the target's General > Supported Destinations section. About one year ago, I was able to run the app on iPhone, iPad and Apple Vision Pro Simulators. Today I tried running it again on Apple Vision Pro with Xcode 26.0.1, but Xcode shows this error: Try again later. Appex bundle at ~/Library/Developer/CoreSimulator/Devices/F6B3CCA8-82FA-485F-A306-CF85FF589096/data/Library/Caches/com.apple.mobile.installd.staging/temp.PWLT59/extracted/problem.app/PlugIns/problemQuickLook.appex with id org.example.problem.problemQuickLook specifies a value (com.apple.quicklook.preview) for the NSExtensionPointIdentifier key in the NSExtension dictionary in its Info.plist that does not correspond to a known extension point. I tried again later a couple times, even after running Clean Build Folder Immediately, without any change. I can reproduce this with a fresh Xcode project to which I add a Quick Look Preview Extension and Apple Vision Pro as a supported destination. The error doesn't happen when running on Apple Vision Pro (Designed for iPad) or iPad Pro 13-inch (M4) destinations. What is the problem? I created FB20448815.
5
0
272
Nov ’25
NEFilterDataProvider.handleNewFlow(_:) gets called with same flow ids multiple times
Since NEFilterFlow.identifier is documented as The unique identifier of the flow., I thought I could use it to store the flow by its identifier in a dictionary in order to retrieve it later. I do this when the system extension pauses a flow because it needs to ask the user whether the flow should eventually be allowed or dropped. But then I noticed that sometimes when allowing a previously paused flow, identified by its identifier, my system extension doesn't find that flow anymore. After some debugging it turned out that this happens because I stored at least one other flow with the same id which, when confirmed, is removed again from the dictionary, so there is no more flow with that identifier waiting in the dictionary. Is it expected that the identifiers are recycled for different flows, or does it mean that the same flow is effectively being passed to .handleNewFlow(_:) multiple times, such as if the extension waited "too long" between pausing a flow and allowing or dropping it? What does this mean?
6
0
736
Nov ’23
Xcode downloads client crash reports for string interpolation (_StringGuts.append)
Xcode contains several crash reports downloaded from users of my app. Thread 1 apparently crashes while performing a string interpolation. All the other threads only contain calls to system code. The String.appendingPathComponent(_:) that appears in the stacktrace is defined as follows: extension String { func appendingPathComponent(_ pathComponent: String) -&gt; String { return pathComponent == "" ? self : self == "" || self == "/" ? "\(self)\(pathComponent)" : "\(self)/\(pathComponent)" } } What could cause such a crash? Thread 1 Crashed: 0 CoreFoundation 0x00007ff81566f9df __CFStringEncodeByteStream + 120 (CFStringEncodings.c:692) 1 Foundation 0x00007ff8164c95aa -[NSString(NSStringOtherEncodings) getBytes:maxLength:usedLength:encoding:options:range:remainingRange:] + 204 (NSStringEncodings.m:341) 2 libswiftCore.dylib 0x00007ff822c6c1e0 String.UTF8View._foreignDistance(from:to:) + 208 (StringUTF8View.swift:507) 3 libswiftCore.dylib 0x00007ff822c56715 _StringGuts.append(_:) + 1445 (StringGutsRangeReplaceable.swift:191) 4 MyApp 0x00000001010c3c0f String.appendingPathComponent(_:) + 15 (&lt;compiler-generated&gt;:0)
6
0
771
Nov ’23