Auto Layout constraint issue with UIKeyboardAssistant on iPad

I ran into this issue with an iOS app I was working on. The app had a UISearchBar above a UITableView, and when tapping a cell it would segue to another view. On iPad, a UIKeyboardAssistantBar would appear if the user typed in the search bar. With text in the search bar, when we segued to the other view, Xcode would dump a bunch of “Unable to simultaneously satisfy constraints” errors:

2018-03-16 11:41:00.734267-0600 testconstraints[82244:11877036] [LayoutConstraints] Unable to simultaneously satisfy constraints.

 Probably at least one of the constraints in the following list is one you don't want. 

 Try this: 

 (1) look at each constraint and try to figure out which you don't expect; 

 (2) find the code that added the unwanted constraint or constraints and fix it. 

 (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 

(

    "<NSAutoresizingMaskLayoutConstraint:0x60000029e550 h=--& v=--& UIKeyboardAssistantBar:0x7fa438f40a00.height == 0   (active)>",

    "<NSLayoutConstraint:0x60000028e740 V:|-(0)-[_UIButtonBarStackView:0x7fa438f436a0]   (active, names: '|':UIKeyboardAssistantBar:0x7fa438f40a00 )>",

    "<NSLayoutConstraint:0x60000028e8d0 V:[_UIButtonBarStackView:0x7fa438f436a0]-(0)-|   (active, names: '|':UIKeyboardAssistantBar:0x7fa438f40a00 )>",

    "<NSLayoutConstraint:0x60000029e1e0 'UISV-canvas-connection' UILayoutGuide:0x6000001b8e20'UIViewLayoutMarginsGuide'.top == _UILayoutSpacer:0x6000003d4460'UISV-alignment-spanner'.top   (active)>",

    "<NSLayoutConstraint:0x60000029e230 'UISV-canvas-connection' UILayoutGuide:0x6000001b8e20'UIViewLayoutMarginsGuide'.bottom == _UILayoutSpacer:0x6000003d4460'UISV-alignment-spanner'.bottom   (active)>",

    "<NSLayoutConstraint:0x60000028e290 'UIView-bottomMargin-guide-constraint' V:[UILayoutGuide:0x6000001b8e20'UIViewLayoutMarginsGuide']-(9)-|   (active, names: '|':_UIButtonBarStackView:0x7fa438f436a0 )>",

    "<NSLayoutConstraint:0x60000028e1f0 'UIView-topMargin-guide-constraint' V:|-(10)-[UILayoutGuide:0x6000001b8e20'UIViewLayoutMarginsGuide']   (active, names: '|':_UIButtonBarStackView:0x7fa438f436a0 )>"

)

Will attempt to recover by breaking constraint 

<NSLayoutConstraint:0x60000029e230 'UISV-canvas-connection' UILayoutGuide:0x6000001b8e20'UIViewLayoutMarginsGuide'.bottom == _UILayoutSpacer:0x6000003d4460'UISV-alignment-spanner'.bottom   (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.

The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

In the end, all I had to do was make sure the searchBar made a call to resignFirstResponder() in prepare(for segue: UIStoryboardSegue, sender: Any?).

Compatibility issue between iOS simulator and BetterTouchTool

TL;DR

It seems BetterTouchTool has a conflict with the iOS simulator in specific user interactions.

The Problem

I have been following along with the excellent and free “Developing iOS 10 Apps with Swift” course on iTunes U (otherwise known as the Stanford course “CS193P, Developing Apps for iOS, Winter 2017”.

I have been working on Programming Project 4: Smashtag Mentions, which involves reading data from Twitter and displaying them on UITableViews. I have checked off most of the required tasks, but ran into a problem today when I opened up my project. My previously working app now crashed almost immediately after starting up. An example trace:

2017-03-23 21:59:52.416 Smashtag[17104:529911] -[UIAccessibilityTextFieldElement convertPoint:fromView:]: unrecognized selector sent to instance 0x600000249f00
2017-03-23 21:59:52.419 Smashtag[17104:529911] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIAccessibilityTextFieldElement convertPoint:fromView:]: unrecognized selector sent to instance 0x600000249f00'
*** First throw call stack:
(
 0 CoreFoundation 0x0000000103e9fd4b __exceptionPreprocess + 171
 1 libobjc.A.dylib 0x00000001016f821e objc_exception_throw + 48
 2 CoreFoundation 0x0000000103f0ff04 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
 3 CoreFoundation 0x0000000103e25005 ___forwarding___ + 1013
 4 CoreFoundation 0x0000000103e24b88 _CF_forwarding_prep_0 + 120
 5 UIAccessibility 0x000000011648fddb -[NSObject(AXPrivCategory) _accessibilityHitTestSupplementaryViews:point:withEvent:] + 477
 6 UIKit 0x000000011630772a -[UITableViewAccessibility _accessibilityHitTest:withEvent:] + 173
 7 UIKit 0x0000000116320b2b -[UIViewAccessibility __accessibilityHitTest:withEvent:] + 1859
 8 UIKit 0x0000000116321979 -[UIViewAccessibility _accessibilityHitTest:withEvent:] + 101
 9 UIKit 0x0000000116320b2b -[UIViewAccessibility __accessibilityHitTest:withEvent:] + 1859
 10 UIKit 0x0000000116321979 -[UIViewAccessibility _accessibilityHitTest:withEvent:] + 101
 11 UIKit 0x0000000116320b2b -[UIViewAccessibility __accessibilityHitTest:withEvent:] + 1859
 12 UIKit 0x0000000116321979 -[UIViewAccessibility _accessibilityHitTest:withEvent:] + 101
 13 UIKit 0x0000000116320b2b -[UIViewAccessibility __accessibilityHitTest:withEvent:] + 1859
 14 UIKit 0x0000000116321979 -[UIViewAccessibility _accessibilityHitTest:withEvent:] + 101
 15 UIKit 0x0000000116339e34 -[UIWindowAccessibility _accessibilityHitTest:withEvent:] + 101
 16 UIAccessibility 0x000000011648337f _copyElementAtPositionCallback + 1722
 17 AXRuntime 0x000000011547e955 _AXXMIGCopyElementAtPosition + 169
 18 AXRuntime 0x0000000115478aab _XCopyElementAtPosition + 311
 19 AXRuntime 0x0000000115487de5 mshMIGPerform + 266
 20 CoreFoundation 0x0000000103e313d9 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 41
 21 CoreFoundation 0x0000000103e31351 __CFRunLoopDoSource1 + 465
 22 CoreFoundation 0x0000000103e29435 __CFRunLoopRun + 2389
 23 CoreFoundation 0x0000000103e28884 CFRunLoopRunSpecific + 420
 24 GraphicsServices 0x0000000106e89a6f GSEventRunModal + 161
 25 UIKit 0x00000001024f3c68 UIApplicationMain + 159
 26 Smashtag 0x00000001010c288f main + 111
 27 libdyld.dylib 0x00000001055cd68d start + 1
 28 ??? 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)

Being new to Swift, Xcode, and iOS development, that was an unfortunate start to my day. Instead of continuing on from my mostly finished assignment, I now had to decipher this trace, and figure out what went wrong.

After some failed initial hopes that this was yet another ephemeral Xcode bug, it seemed there was a real problem here. Cleaning, re-building did nothing to fix the error. Wiping the (simulated) device and re-deploying the app did not help. Downloading and trying on an iOS 10.1 device also did not work.

My Google-fu was failing, so I had to try and solve this on my own. I traced that the exception was triggered after some user interaction. The view I was interacting with was an initially empty UITableView with a search UITextField at the top. It seemed the exception was getting consistently triggered when my focus entered this text field. Interestingly, moving this text field to the bottom of the table prevented the error.

I decided to build a new project from scratch, in case something I had done on the storyboard or my app code was triggering the issue. Sure enough, the simplest app with only a UITableView and UITextField in the header would trigger this exception. Great…but what next?

After some flailing, and not getting anywhere, I thought I might try the latest Xcode 8.3 beta, and see if that got me anywhere. Plus, it was time for dinner.

The Breakthrough

After downloading and installing the latest Xcode 8.3 beta 5, I encountered the same error. So I would have to try a different approach.

It seemed the crash was related to user interaction, keyboard/mouse input into the simulator. Maybe another utility on my system was interfering with the simulator?

I looked at my Status menu and noticed BetterTouchTool sitting there. I quit. Ran my app, and the exception was gone.

Opened back up BetterTouchTool, put the focus in the simulator, and the exception was back.

Well, it seems BetterTouchTool 2.071 (608) has some conflict with the iOS simulator. I will have to do some more experimenting to see exactly what the conflict is, and if there is a specific setting that is causing the issue. For now though, I will continue with BetterTouchTool turned off.