How to solve " AppKit Consistency error"

MichaelBothMichaelBoth AUMember ✭✭✭

Recently I've converted our Mac Class app (that has been working fine 'in the field' for a year or so) to the Unified API, and since then we've been experiencing occasional exceptions along the line of "AppKit Consistency error: you are calling a method that can only be invoked from the UI thread". I'm not using any threads or async techniques, so I'm a bit puzzled as to how / why this is happening. As there any methodical way I can resolve this issue?

Best Answers

Answers

  • I think it could originate from a callback used in a AppKit api call.

    Look through your code and put any UI related code after a callback in:

    InvokeOnMainThread(() => { 
        // UI CODE HERE
    });
    
  • MichaelBothMichaelBoth AUMember ✭✭✭

    Sjoerd and Chris - thanks for the responses. Chris, especially, I appreciate it, your reply is very comprehensive. I've been working through the application, looking for and - hopefully - fixing issues. Guess I've had the luck of the devil so far as I seem to overlooked these problems for the initial release of the application. :(

  • MichaelBothMichaelBoth AUMember ✭✭✭

    The full exception information we have logged is:
    Message: AppKit Consistency error: you are calling a method that can only be invoked from the UI thread. Source: Xamarin.Mac at AppKit.NSApplication.EnsureUIThread () [0x0001f] in <379018de32e5466391eaea99f15edc7a>:0 at AppKit.NSWindow.get_WeakDelegate () [0x00001] in <379018de32e5466391eaea99f15edc7a>:0 at AppKit.NSWindow.get_Delegate () [0x00001] in <379018de32e5466391eaea99f15edc7a>:0 at AppKit.NSWindow.EnsureNSWindowDelegate () [0x00001] in <379018de32e5466391eaea99f15edc7a>:0 at AppKit.NSWindow.remove_WillClose (System.EventHandler value) [0x00001] in <379018de32e5466391eaea99f15edc7a>:0 at <productname>.View.Core.FSBaseListForm.Finalize () [0x00000] in <8817c6b956f7423c90ba1431191f9134>:0

  • MichaelBothMichaelBoth AUMember ✭✭✭

    Ugh ... finally the touchy anti-spam system releases all three versions of my post I tried to place here a week ago.

  • MichaelBothMichaelBoth AUMember ✭✭✭

    @ChrisHamons said:
    Sorry for the trouble @MichaelBoth I just double checked and you are verified now, so hopefully the spam system will leave you alone. I also culled the duplicate posts. PM me if it rears it's ugly head again.

    Thanks again, Chris.

    Reading this, we're calling stuff from FSBaseListForm's Dispose method that uses AppKit, which is not kosher.

    Pretty close - I was unsubbing from an event:
    WillClose -= HandleWillClose;
    ... in the destructor for FSBaseListForm. I've since worked out that was a bad idea!

    In most cases, it is not a great idea to do anything w\ AppKit in Dispose methods. Even if you BeginInvokeOnMainThread over to the UI thread, it's still happening at an unknown "random" time, and isn't the safest pattern.

    However ... where is the best place to unsub from an event handler? I've read that 'Dispose' is the best (in a general C# sense, not necessarily for Xamarin Mac), and have also read on these forums it's better not to use event subscriptions at all, but rather create a delegate for the object and do all the handling in there.

  • ChrisHamonsChrisHamons USForum Administrator, Xamarin Team Xamurai
    edited November 2016

    So under the hood, in the deep dark magic of generator.cs we do magic to convert the obj-c pattern into "nice" C# event. While I'm happy to go into detail if you wish, the rough idea is when you sub for a C# event:

    • Make sure someone else didn't assign a delegate already. If so, scream.
    • Make our own delegate that overrides everything and has a C# event for each
    • Assign that to the delegate
    • When we get the call, invoke the correct event

    Using the C# events is fine, especially for simple use cases, but when things get complicated I often suggest falling back to implementing the correct interface and assigning yourself to the delegate. It can make memory management significantly less tricky.

    Often the correct answer on when to unsub is when you leave the visual tree (assuming you aren't doing magic and dynamic removing/readding youself to the visual tree). ViewWillAppear/ViewWillDisappear on NSViewController for example.

  • MichaelBothMichaelBoth AUMember ✭✭✭

    Thanks, Chris, I reviewed the entire code for the project in light of your comments (and other handy posts I found on the forums), making changes where necessary (i.e. in many places!) and testers are reporting no more issues.

Sign In or Register to comment.