Nathanael Carper Nathanael Carper - 2 months ago 57
Swift Question

Weird error when executing code in Xcode 8

I'm getting an error when I run a certain type of code in my macOS project and I'm not sure how to fix it. Here's the error I'm getting:

I'm getting this weird error.

This is the code that's giving me the error:

func AlarmAlertBox() {
let alert = NSAlert()
alert.messageText = AlarmTitleVar
alert.informativeText = DescriptionVar
alert.addButton(withTitle: "OK")
let result = alert.runModal()
switch(result) {
case NSAlertFirstButtonReturn:
print("OK", terminator: "")
default:
break
}
}


EDIT (was requested to add this): I am running this code in a Timer function:

_ = DispatchQueue.global().async {

var ReminderNum = 1

self.ReminderCheckON = true

while self.ReminderCheckON == true {
let int : Int = ReminderNum
let ReminderStr = String(int)
let ReminderStuff = UserDefaults().string(forKey: ReminderStr) ?? ""
ReminderNum = ReminderNum + 1
if ReminderStuff == "" {
self.ReminderCheckON = false
ReminderNum = 1
}

if ReminderStuff.range(of: self.ReminderCurrentTime.stringValue) != nil
{
print("Alarm Time!")
if self.DeleteAlarm == "ON" {
UserDefaults().set("-", forKey: ReminderStr)
self.AlarmsEnd()
self.UpcomingAlarms.stringValue = ""
self.ReminderList.removeAllItems()
self.AlarmsCheck()
}

self.AlarmTitleVar = UserDefaults().string(forKey: "T" + ReminderStr) ?? ""

self.DescriptionVar = UserDefaults().string(forKey: "D" + ReminderStr) ?? ""

self.ReminderCheckON = false

self.ReminderRingToneVar = UserDefaults().string(forKey: "R" + ReminderStr) ?? "Alarm"

if self.ReminderRingToneVar == "Spring" {
let Spring = NSSound(named: "Spring")
Spring!.play()
self.AlarmAlertBox()
Spring!.stop()
}

if self.ReminderRingToneVar == "Alarm" {
let Alarm = NSSound(named: "RingDing")
Alarm!.play()
self.AlarmAlertBox()
Alarm!.stop()
}

if self.ReminderRingToneVar == "Strings" {
let Strings = NSSound(named: "Strings")
Strings!.play()
self.AlarmAlertBox()
Strings!.stop()
}

if self.ReminderRingToneVar == "Happy" {
let Happy = NSSound(named: "Happy")
Happy!.play()
self.AlarmAlertBox()
Happy!.stop()
}

if self.ReminderRingToneVar == "Bouncy" {
let Bouncy = NSSound(named: "Joshua's Creation")
Bouncy!.play()
self.AlarmAlertBox()
Bouncy!.stop()
}

if self.ReminderRingToneVar == "Star-Spangled Banner" {
let StarSpangledBanner = NSSound(named: "The_United_States_Army_Old_Guard_Fife_and_Drum_Corps_-_02_-_United_States_National_Anthem_The_Star_Spangled_Banner")
StarSpangledBanner!.play()
self.AlarmAlertBox()
StarSpangledBanner!.stop()
}
}
}
}


The debug window is giving me this:


2016-09-27 13:35:00.856685 Clock Pro[2441:115266] [General] An uncaught exception was raised

2016-09-27 13:35:00.856732 Clock Pro[2441:115266] [General]
2016-09-27 13:35:00.856803 Clock Pro[2441:115266] [General]
2016-09-27 13:35:00.856885 Clock Pro[2441:115266] * Terminating app due to uncaught exception 'NSGenericException', reason: '-[NSAlert runModal] may only be invoked from the main thread. Behavior on other threads is undefined.'
*
First throw call stack:
(
0 CoreFoundation 0x00007fffaaefd52b exceptionPreprocess + 171
1 libobjc.A.dylib 0x00007fffbf5d5cad objc_exception_throw + 48
2 CoreFoundation 0x00007fffaaf7ba0d +[NSException raise:format:] + 205
3 AppKit 0x00007fffa9323b44 _NSRunModal + 103
4 AppKit 0x00007fffa8dd757d -[NSAlert runModal] + 270
5 Clock Pro 0x000000010005aab8 _TFC9Clock_Pro14ViewController13AlarmAlertBoxfT_T_ + 344
6 Clock Pro 0x000000010002d053 _TFFC9Clock_Pro14ViewController5TimerFT_T_U_FT_T_ + 6563
7 Clock Pro 0x000000010000f317 _TTRXFo___XFdCb_
+ 39
8 libdispatch.dylib 0x00000001009fe74d _d
libc++abi.dylib: terminating with uncaught exception of type NSException


I use this code a lot in my project, and I haven't had problems those other lines of code. I've tried several things to fix this problem or work around it but I haven't been successful. Any help would be great.

Answer

As already commented by Hamish, you need to run all UI related tasks on the main tread. It's not super difficult, someone who uses background thread should know:

Something like this:

    if self.ReminderRingToneVar == "Spring" {
        DispatchQueue.main.async {
            let spring = NSSound(named: "Spring")
            spring!.play()
            self.AlarmAlertBox()
            spring!.stop()
        }
    }

By the way, you'd better follow the standard coding rule of Swift, if you have any chance to share your code in a public space (I mean, including to write a question in a Q&A site): Use capital-CamelCase only for types.

Your code is hard to read.

Comments