2016년 10월 27일 목요일

우연히 발견한 macOS 10.12 NSAlert 버그(?) 삽질기

macOS 10.12 시에라가 업데이트 된 후로 개발 중이던 맥 앱에서 DETECTED_MISSING_CONSTRAINTS 예외가 발생하는 상황을 겪었다. 물론 10.11 에서 개발할 당시에는 잘 돌아가던 앱이었는데 10.12 부터 이런 오류가 발생하기 시작했다.

다행인 점은 죽지는 않고 경고 수준으로 끝나는 오류였다는 점이다. 사람마다 느끼는 점은 다르겠지만, 나에겐 찝찝함을 남기는 메시지가 떴기에 그냥 넘길 수가 없었다.

아래는 해당 문제가 발생하는 코드 예제이다. 코드를 보면 알 수 있겠지만 텍스트필드를 박아서 텍스트를 입력 받기 위한 경고창을 뛰우는 코드이다.
let alert = NSAlert()
alert.messageText = "Enter new name"
alert.addButton(withTitle: "Ok")
alert.addButton(withTitle: "Cancel")
    
let input = NSTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 24))
alert.accessoryView = input
    
let button = alert.runModal()
    
if button == NSAlertFirstButtonReturn {
  // Ok
  input.validateEditing()
  doSomeWork(withName: input.stringValue)
}
else {
  // Cancel
  dontSomeWork()
}

이 코드가 실행되면 아래와 같은 오류가 발생한다.
[Layout] Detected missing constraints for <NSTextField: 0x????????????>. It cannot be placed because there are not enough constraints to fully define the size and origin. Add the missing constraints, or set translatesAutoresizingMaskIntoConstraints=YES and constraints will be generated for you. If this view is laid out manually on macOS 10.12 and later, you may choose to not call [super layout] from your override. Set a breakpoint on DETECTED_MISSING_CONSTRAINTS to debug. This error will only be logged once.

이 오류가 발생한다고 해서 앱이 죽거나 하지는 않는다. 그냥 경고를 표시하는 수준에서 끝나고 앱 자체는 정상적으로 실행된다. 물론 앞서 이야기 했다싶이 찝찝해서 해결하지 않고서는 못 베기겠던 문제다.

왜 10.12 부터 이런 문제가 생기는지 이유는 잘 모르겠지만, DETECTED_MISSING_CONSTRAINTS 심볼 브레이크 포인트를 잡아서 문제를 일으켜서 디버그 창에 'po $arg1'을 입력해 보면 NSTextField 인스턴스가 나타난다. 그래서 문제를 일으킨 건 accessoryView 로 지정된 input(NSTextField)이라고 단정했다.

하지만 문제 해결을 위해 별의별 방법을 동원해 봤지만 문제는 해결되지 않았다. 위의 에러메시지에 등장하는 translateAutoresizingMaskIntoConstraints 값을 true 로 세팅하는 건 아무 도움이 되지 않았다.

심지어 NSTextField 를 오버라이딩 한 것도 없기 때문에 super.layout() 을 실행하지 않기라는 방법은 쓸 수 조차 없었다.

superview 가 없기 때문에 오토레이아웃을 쓰는 것도 여의치 않았다. 그냥 자기 기준으로 width 와 height 에 해당하는 NSLayoutConstraints 를 넣어 보는 것도 해결에 도움이 되지 않았다.

Solution - 우연히 발견한 해결책

핵심부터 정리해 보자. NSAlert 의 informativeText 값을 지정해 주면 위의 문제가 해결된다.
let alert = NSAlert()
alert.messageText = "Enter new name"
alert.informativeText = "blah blah blah...what?!"
...
이렇게 하면 message 와 텍스트 필드 사이에 위의 문자열 내용이 표시된다. 그리고 신기하게도 앞서 나오던 missing constraints 문제가 발생하지 않는다.


개인적으론 macOS 의 버그가 아닐지 의심된다. informativeText 가 공백("")일 경우 이를 표시하지 않는 과정에서 레이아웃에 필요한 정보가 빠진게 아닐까 추측만 가능하다.

일단 이 문제는 macOS 10.12.1 까지도 발생하고 있다. 추후 macOS 가 업데이트 되면 더 시험해 볼 여지가 있을 것 같다.

댓글 없음 :