Gargoyle Gargoyle - 1 year ago 90
Swift Question

Cocoa crash from non-existant NSViewController

In my mac app I'm presenting a login window (in an NSViewController via a

segue). There's an OK button on it, and when I press that I make a network call. At the end of the network call I do some stuff and then dismiss the sheet. The sheet does in fact disappear.

Now back on my NSViewController that presented the login window I start scrolling an NSTableView up and down. I'm getting a CoreAnimation warning about uncommitted CATransaction messages. Sometimes it causes a crash, sometimes it doesn't.

This is the stack trace I get:

CoreAnimation: warning, deleted thread with uncommitted CATransaction; created by:

0 QuartzCore 0x00007fff8befe69a _ZN2CA11Transaction4pushEv + 318

1 QuartzCore 0x00007fff8befe19a _ZN2CA11Transaction15ensure_implicitEv + 276

2 QuartzCore 0x00007fff8bf04719 _ZN2CA5Layer13thread_flags_EPNS_11TransactionE + 37

3 QuartzCore 0x00007fff8bf04668 _ZN2CA5Layer4markEPNS_11TransactionEjj + 64

4 QuartzCore 0x00007fff8bf0612b _ZN2CA5Layer25set_needs_display_in_rectERK6CGRect + 333

5 QuartzCore 0x00007fff8bf05fdc -[CALayer setNeedsDisplayInRect:] + 25

6 AppKit 0x00007fff91aff2e8 _NSBackingLayerSetNeedsDisplayInRect + 319

7 AppKit 0x00007fff91aff1a3 -[_NSBackingLayer setNeedsDisplayInRect:] + 61

8 QuartzCore 0x00007fff8bf05f9d -[CALayer setNeedsDisplay] + 62

9 AppKit 0x00007fff91aff98b -[NSView(NSInternal) _setLayerNeedsDisplayInViewRect:] + 606

10 AppKit 0x00007fff91acd88e NSViewSetNeedsDisplayInRect + 945

11 AppKit 0x00007fff91acd4d6 -[NSView setNeedsDisplayInRect:] + 48

12 AppKit 0x00007fff91acd2ed -[NSView setNeedsDisplay:] + 81

13 AppKit 0x00007fff91e48905 -[NSProgressIndicator setDoubleValue:] + 338

14 Digital Traffic System 0x0000000100063fba _TFFFC22Digital_Traffic_System19LoginViewControllerP33_5EDE918279C9F0B1F3EE7B966507D60715okButtonPressedFCSo8NSButtonT_U_FGSqPs9AnyObject__T_U_FTSSSi_T_ + 266

15 Digital Traffic System 0x0000000100064282 _TTRXFo_oSSdSi_dT__XFo_iTSSSi__iT__ + 66

Line 14 shows my "okButtonPressed" method from the LoginViewController, and then on line 13 I see it talking about my NSProgressIndicator trying to have a value set. It shouldn't even exist at this point :(

Here's what that method looks like:

final class LoginViewController: NSViewController, HasMOC {
@IBOutlet private weak var progressBar: NSProgressIndicator!

@IBAction private func okButtonPressed(sender: NSButton) {
sender.enabled = false


// Webserver uses Alamofire to make async web calls.
Webserver.sharedInstance.startupData {
[unowned self] data in

defer { sender.enabled = true }

guard let json = data as? [[[String : AnyObject]]] else {
NSAlert.okWithMessage("Failed to download data from web server.")

self.progressBar.hidden = false

DataImporter.importData(json, managedObjectContext: self.managedObjectContext,
onType: {
[weak self] str, max in
self?.progressBar.doubleValue = 0
self?.progressBar.maxValue = Double(max)
self?.progressText.stringValue = str
}, onEntity: {
[weak self] in
}, onComplete: {
[weak self] in
(NSApplication.sharedApplication().delegate as! AppDelegate).showHideReportMenu()



I use unowned in the initial block because this window can't disappear unless the web call has returned. There's no button to make it go away manually.

Answer Source

Your service callbacks are executed on non-UI thread and you try to update UI elements.

You should move those onto Main Queue, i.e.:

NSOperationQueue.mainQueue().addOperationWithBlock {
        [unowned self] in
        //Your code changing UI elements, kicking animations .e.t.c
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download