I am quite sure about that, because I only rely on NSDocumentController to serve me the files. If I understand correctly, NSDocument should support sandboxing out-of-the-box?
I can suppress "Scoped bookmarks can only be created for existing files or directories" by overriding autosavedContentsFileURL and creating a blank file before returning its URL, but it doesn't seem to affect the sandbox_extension_consume 12 error.
Post
Replies
Boosts
Views
Activity
For some reason I can't edit the question, but further investigations pointed out that the bad access crash was not related to the sandbox extension error. BUT – after opening couple of documents, this error stops the app from saving anything, including even the files that were already open. It happens quite randomly, and gives out an alert saying that the file can't be accessed. All files which are open are then deadlocked.
Asynchronous saving seems to result in weird thread safety problems that are not really covered by NSDocument documentation, or even discussed anywhere. So, for anyone else struggling with this:
In your dataOfType: method, be careful not to fetch anything, which could be mutating while an edit is made. This is the basic solution, but apparently it's more complicated than that.
I still haven't resolved the issue completely, and have to go through thousands of lines of code to find possible culprits for this thread un-safety. I tried to make a buffer/cache which only gets updated when an edit has been made, and in dataOfType: I also made sure to make copies of that data. This doesn't help, because it's still completely possible that the data was altered on the same millisecond, causing a new race condition.
Similar issue is discussed here: https://developer.apple.com/forums/thread/114579
It hints to something about changes in layer backing, but no further information is available.
It appears that this is related to scaled resolution. On some systems, default scaling causes the issue, while on others you can set any scaling and never experience a crash.
A trick which fixes this crash on all systems is to subclass NSClipView, override its layout method and never call super methods. Just don't layout the NSClipView. I'm not sure if this will cause further damage along the way, but works as a quick and dirty fix to crashes. Mileage may vary.
To anyone wondering about this:
Something in view layout pipeline was changed in Big Sur or Monterey so that setting the frame of document view will cause NSClipView to invalidate its layout. I had overridden setFrame in my NSTextView, and as the frame was set, NSClipView would cause an infinite loop by calling setFrame again.
Sample project:
https://www.dropbox.com/s/t220cn8orooorwb/WebkitPDFTest.zip?dl=1
The issue was [printOperation runOperation] / printOperation.runOperation(), which simply does not work.
Instead, you need to call the strangely named runOperationModalForWindow. Although the method name refers to a modal window, you don't need to display the modal. This method makes the print operation run asynchronously (?) which modern WebKit likes more.
Apple, please, please, please document this somewhere.
// Create print operation
NSPrintOperation *printOperation = [webView printOperationWithPrintInfo:printInfo];
// Set web view bounds - without this, the operation will crash
printOperation.view.frame = NSMakeRect(0,0, printInfo.paperSize.width, printInfo.paperSize.height);
// Print it out
[printOperation runOperationModalForWindow:self.window delegate:self didRunSelector:@selector(printOperationDidRun:success:contextInfo:) contextInfo:nil];
You also need to have a handler method to catch the results:
- (void)printOperationDidRun:(id)operation success:(bool)success contextInfo:(nullable void *)contextInfo {
// Do something with your print
}
Oh well. Here's a simple way to render a UITextView as real text into a PDF using TextKit 2. It currently supports only one text attachment per paragraph, because you apparently can't use fragment.draw(at:origin:) to display attachments.
let renderer = UIGraphicsPDFRenderer(bounds: pageRect, format: format)
let data = renderer.pdfData { (context) in
let cgContext = context.cgContext
textView.textLayoutManager.enumerateTextLayoutFragments(from: location, options: [.ensuresLayout, .estimatesSize, .ensuresExtraLineFragment], using: { fragment in
let frame = fragment.layoutFragmentFrame
let origin = page.textView?.frame.origin ?? CGPointZero
var actualFrame = frame
actualFrame.origin.x += origin.x
actualFrame.origin.y += origin.y
if let provider = fragment.textAttachmentViewProviders.first, let view = provider.view {
// Draw a text attachment
let attachmentFrame = fragment.frameForTextAttachment(at: fragment.rangeInElement.location)
actualFrame.origin.y += attachmentFrame.origin.y
cgContext.saveGState()
cgContext.translateBy(x: actualFrame.origin.x, y: actualFrame.origin.y)
view.layer.render(in: cgContext)
cgContext.restoreGState()
return true
} else {
// Draw a normal paragraph
fragment.draw(at: origin, in: cgContext)
}
return true
})
}
I have no idea why Apple decided make this the default behavior in UITextView, I doubt most people would want their text views to be rasterized in PDFs.
The exact issue persists to this day. Trying to remove a localization causes an error, and changes can't be saved. This is somewhat irritating, as while my app is localized to numerous languages, updating the texts on App Store Connect for each version is an unneeded hassle.