CFRunLoop in Swift Command Line Program

Sto scrivendo un’applicazione a riga di comando in Swift usando un framework di terze parti che (se ho capito bene il codice) si affida ai callback GCD per completare determinate azioni quando un socket riceve i dati. Per capire meglio il framework, ho giocato con una applicazione Cocoa di esempio che l’autore del framework ha scritto per andare avanti con il framework.

Poiché l’applicazione di esempio è un’applicazione Cocoa, i loop di esecuzione vengono gestiti automaticamente. Sto includendo frammenti di codice dall’applicazione di esempio (licenza MIT) per dare un’idea di come funziona:

class AppDelegate: NSObject, NSApplicationDelegate { var httpd : Connect! func startServer() { httpd = Connect() .onLog { [weak self] in // unowned makes this crash self!.log($0) } .useQueue(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) 

  httpd.listen(1337) } 

  func applicationDidFinishLaunching(aNotification: NSNotification?) { startServer() ... } } 

Mi piacerebbe modificare l’applicazione di esempio per l’esecuzione dalla riga di comando. Quando inserisco la funzione startServer () in un’applicazione a riga di comando, viene eseguita, ma il socket viene immediatamente chiuso dopo l’apertura e il programma termina l’esecuzione con un codice di uscita 0. Si tratta di un comportamento previsto, poiché non vi sono cicli di esecuzione in un progetto della riga di comando Xcode, e quindi il programma non sa aspettare che il socket riceva i dati.

Credo che il modo corretto per far rimanere aperto il socket e che il programma funzioni continuamente sarebbe quello di inserire il thread principale in un CFRunLoop. Ho esaminato la documentazione di Apple e, fatta eccezione per il riferimento API di base, non c’è nulla sul threading in Swift. Ho esaminato le risorse di terze parti, ma tutte coinvolgono thread alternativi nelle applicazioni iOS e Cocoa. Come implementare correttamente un CFRunLoop per il thread principale?

Sembra che la risposta di Martin R dovrebbe funzionare, tuttavia sono riuscito a mantenere aperto il socket con una singola chiamata di funzione. Alla fine della funzione startServer (), inserisco la riga:

 CFRunLoopRun() 

Che ha funzionato.

Il riferimento alla class NSRunLoop ha un esempio per un semplice runloop:

 BOOL shouldKeepRunning = YES; // global NSRunLoop *theRL = [NSRunLoop currentRunLoop]; while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); 

che può essere tradotto in Swift:

 var shouldKeepRunning = true // global let theRL = NSRunLoop.currentRunLoop() while shouldKeepRunning && theRL.runMode(NSDefaultRunLoopMode, beforeDate: NSDate.distantFuture()) { } 

In alternativa, potrebbe essere sufficiente chiamare

 dispatch_main() 

Aggiornamento per Swift 3.1:

 let theRL = RunLoop.current while shouldKeepRunning && theRL.run(mode: .defaultRunLoopMode, before: .distantFuture) { } 

o

 dispatchMain()