I was able to confirm with a customer of mine that calling copyfile with a source file that is a symbolic link on a NTFS partition always causes the error
NSPOSIXErrorDomain 12 Cannot allocate memory
They use NTFS drivers from Paragon.
They tried copying a symbolic link from NTFS to both APFS and NTFS with the same result. Is this an issue with macOS, or with the NTFS driver?
Copying regular files on the other hand always works. Copying manually from the Finder also seems to always work, both with regular files and symbolic links, so I'm wondering how the Finder does it.
Here is the sample app that they used to reproduce the issue. The first open panel allows to select the source directory and the second one the destination directory. The variable filename holds the name of the symbolic link to be copied from the source to the destination. Apparently it's not possible to select a symbolic link directly in NSOpenPanel, as it always resolves to the linked file.
@main
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
let openPanel = NSOpenPanel()
openPanel.canChooseDirectories = true
openPanel.canChooseFiles = false
openPanel.runModal()
let filename = "Modules"
let source = openPanel.urls[0].appendingPathComponent(filename)
openPanel.runModal()
let destination = openPanel.urls[0].appendingPathComponent(filename)
do {
let state = copyfile_state_alloc()
defer {
copyfile_state_free(state)
}
var bsize = UInt32(16_777_216)
if copyfile_state_set(state, UInt32(COPYFILE_STATE_BSIZE), &bsize) != 0 {
throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno))
}
if 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(source.path, destination.path, state, copyfile_flags_t(COPYFILE_NOFOLLOW)) != 0 {
throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno))
}
} catch {
let error = error as NSError
let alert = NSAlert()
alert.messageText = "\(error.localizedDescription)\n\(error.domain) \(error.code)"
alert.runModal()
}
}
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)
}
return COPYFILE_CONTINUE
}
}
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
On macOS, we have didMountNotification but there doesn't seem to be an equivalent for iOS. Is there a way to be notified when a volume is mounted on iOS? I would like to use it in my iOS app I'm currently porting from macOS, which starts a synchronization from the volume (which has been previously selected in a NSOpenPanel) as soon as it's mounted.
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
I can run my app on my MacBook Pro running macOS 10.14.6, but an App Store user reached out to report that it crashes immediately when launching it. When that user runs it from the Terminal, it prints out this:Illegal instruction: 4
My Mac app uses many NSTextViews just fine for printing a long string, but trying to do the same on iOS with many UITextViews and associated UIViewPrintFormatters just causes a crash.*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff23c7127e __exceptionPreprocess + 350
1 libobjc.A.dylib 0x00007fff513fbb20 objc_exception_throw + 48
2 CoreFoundation 0x00007fff23d03ab1 _CFThrowFormattedException + 194
3 CoreFoundation 0x00007fff23b83bf9 -[__NSArrayM objectAtIndex:] + 169
4 UIKitCore 0x00007fff48029503 -[UITextViewPrintFormatter rectForPageAtIndex:] + 86
5 UIKitCore 0x00007fff4806bc79 __57-[UIPrintPageRenderer drawPrintFormatter:forPageAtIndex:]_block_invoke + 41
6 UIKitCore 0x00007fff4806bcf9 __57-[UIPrintPageRenderer drawPrintFormatter:forPageAtIndex:]_block_invoke.43 + 29
7 libdispatch.dylib 0x000000010e4f6d48 _dispatch_client_callout + 8
8 libdispatch.dylib 0x000000010e505b24 _dispatch_async_and_wait_invoke + 175
9 libdispatch.dylib 0x000000010e4f6d48 _dispatch_client_callout + 8
10 libdispatch.dylib 0x000000010e504de6 _dispatch_main_queue_callback_4CF + 1500
11 CoreFoundation 0x00007fff23bd4049 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
12 CoreFoundation 0x00007fff23bceca9 __CFRunLoopRun + 2329
13 CoreFoundation 0x00007fff23bce066 CFRunLoopRunSpecific + 438
14 GraphicsServices 0x00007fff384c0bb0 GSEventRunModal + 65
15 UIKitCore 0x00007fff48092d4d UIApplicationMain + 1621
16 myApp 0x000000010e1ca61b main + 75
17 libdyld.dylib 0x00007fff5227ec25 start + 1
18 ??? 0x0000000000000001 0x0 + 1
)Here's the code:class ViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
let printController = UIPrintInteractionController.shared
let printPageRenderer = PrintPageRenderer()
printController.printPageRenderer = printPageRenderer
printController.present(animated: true)
}
}
class PrintPageRenderer: UIPrintPageRenderer {
let layoutManager = NSLayoutManager()
let textStorage = NSTextStorage(string: "fhdjksalhfj dskla fjf")
var textViews = [UITextView]()
override func prepare(forDrawingPages range: NSRange) {
DispatchQueue.main.sync {
for (i, textView) in textViews.enumerated() {
let printFormatter = textView.viewPrintFormatter()
addPrintFormatter(printFormatter, startingAtPageAt: i)
}
}
}
override var numberOfPages: Int {
textStorage.addLayoutManager(layoutManager)
let size = CGFloat(50)
for _ in 0..<2 {
let textContainer = NSTextContainer(size: CGSize(width: size, height: size))
layoutManager.addTextContainer(textContainer)
let textView = UITextView(frame: CGRect(x: 0, y: 0, width: size, height: size), textContainer: textContainer)
textViews.append(textView)
}
return textViews.count
}
}My goal is printing a document from an attributed string and showing a custom header and footer on each page.
I haven‘t yet found an official Apple website that lists older macOS versions. Fortunately, I have kept the installers on a separate external storage after each major upgrade, but when launching them from a macOS version newer that the installer itself, macOS shows an error that it‘s not possible to install it. What is the official way of installing older macOS versions so that I can test my App Store apps?
When a file named a.txt is not downloaded locally, the iCloud Drive folder actually only contains a placeholder named .a.txt.icloud (that is still displayed as a.txt by the Finder) with a very small file size, something like 176 bytes. How can I get the original file size, like the Finder displays?
I would like to install macOS 14 Sonoma on an external partition, but I couldn't find an installer on the developer website. It seems that it's only possible to upgrade from the System Settings, so I thought I would install Ventura first on the separate partition and then upgrade to Sonoma.
I downloaded the Ventura installer from the App Store and selected the external partition during the installation, but when the Mac reboots, it just reboots into my main macOS partition. If I shut down and hold the power button pressed to select the boot partition (I have a M1 MacBook Pro 14" 2022), then nothing happens: an indeterminate progress indicator spins for a couple seconds, the partition list remains there and I can only successfully boot into my main partition again. I also tried running the installer from another macOS Ventura and macOS Monterey partition I had installed last year, but when I select the partition on which to install the new Ventura, nothing happens and I can only quit the installer.
Is there a way to debug all these issues? How can I install macOS Sonoma on a separate partition without overwriting my main macOS Ventura install?
I keep getting crash reports in Xcode for one of my macOS apps published on the App Store. Actually it's not the main app that crashes, but the embedded Finder Sync extension. The crash reports indicate that this source code line
static var appGroupSaveDirectoryUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: identifier)!.appendingPathComponent("Library/Application Support/somedata")
crashes with error
Swift runtime failure: Unexpectedly found nil while unwrapping an Optional value + 0 (<compiler-generated>:0)
That line is a static variable defined in a class that is included in the main app as well as the Finder extension.
The documentation reads
In iOS, the value is nil when the group identifier is invalid. In macOS, a URL of the expected form is always returned, even if the app group is invalid
If the documentation says that the method call can never be nil, why am I getting this crash? Is it a bug or is the documentation wrong, or am I doing something wrong? And why does the Finder Sync extension crash and not the main app? I cannot reproduce this crash with the App Store app or within Xcode and Console shows no crash reports for the app on my Mac.
For the current app, I can get the Application Scripts directory with FileManager.url(for: .applicationScriptsDirectory, in: .userDomainMask, appropriateFor: nil, create: true), but I cannot find a similar API for application groups. Does one exist, or do I have to construct the URL manually? That would probably be ~/Library/Application Scripts/[app group id], but there doesn't seem to be a FileManager API to access ~ either, as FileManager.default.homeDirectoryForCurrentUser returns /Users/username/Library/Containers/[app id]/Data/.
With a Button I can directly copy contents to the clipboard via UIPasteboard.general.setObjects(objects), but this is iOS-specific so I was hoping that ShareLink would allow me to share items in a platform-independent way. Is it possible to show a Copy item in the ShareLink sheet?
import SwiftUI
struct ContentView: View {
let myType = MyType()
var body: some View {
ShareLink(item: myType, preview: SharePreview(myType.text))
}
}
class MyType: Transferable {
let text = "asdf"
static var transferRepresentation: some TransferRepresentation {
ProxyRepresentation { myType in
return myType.text
}
}
}
#Preview {
ContentView()
}
This crash report for one of my apps was downloaded by Xcode. Apparently the app crashed while releasing an object of type Scan.File, which is a Swift class held in an array in the Scan.Directory class. I'm not doing any manual reference counting or low-level stuff with that object.
What could cause such a crash?
crash.crash
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))")
}
}
}
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)
}
}
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?