2014-12-15 32 views
8

Ich möchte wissen, ob es eine Methode gibt, meine Anwendung programmgesteuert neu zu starten. Es ist eine Mac OS App und ich arbeite mit Xcode 6 in Swift.Programm programmgesteuert neu starten

Die Prozedur ist einfach, zu einem bestimmten Zeitpunkt möchte ich meine App neu starten. Ich denke, ich brauche einen einfachen Helfer, aber ich bin mir nicht sicher.

+0

Sie th finden konnten, Verwenden Sie den Swift-Wrapper für die Funktionen POSIX [exec *] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html) –

Antwort

9

Ja, Sie brauchen ein Hilfswerkzeug. Hier ist der Vorgang:

  1. Erstellen Sie Helfer "Command Line Tool" Ziel in Ihrem Projekt. Zum Beispiel, mit dem Namen "Relaunch"

    Relaunch/main.swift:

    import AppKit 
    
    // KVO helper 
    class Observer: NSObject { 
    
        let _callback:() -> Void 
    
        init(callback:() -> Void) { 
         _callback = callback 
        } 
    
        override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) { 
         _callback() 
        } 
    } 
    
    
    // main 
    autoreleasepool { 
    
        // the application pid 
        let parentPID = atoi(C_ARGV[1]) 
    
        // get the application instance 
        if let app = NSRunningApplication(processIdentifier: parentPID) { 
    
         // application URL 
         let bundleURL = app.bundleURL! 
    
         // terminate() and wait terminated. 
         let listener = Observer { CFRunLoopStop(CFRunLoopGetCurrent()) } 
         app.addObserver(listener, forKeyPath: "isTerminated", options: nil, context: nil) 
         app.terminate() 
         CFRunLoopRun() // wait KVO notification 
         app.removeObserver(listener, forKeyPath: "isTerminated", context: nil) 
    
         // relaunch 
         NSWorkspace.sharedWorkspace().launchApplicationAtURL(bundleURL, options: nil, configuration: [:], error: nil) 
        } 
    } 
    
  2. hinzufügen Products/relaunch Binary "Copy Bundle Ressourcen" in dem Hauptanwendungsziel.

  3. Fügen Sie relaunch Ziel zu "Zielabhängigkeiten" im Hauptanwendungsziel hinzu.

    screenshot

  4. relaunch Funktion in der Hauptanwendung hinzufügen.

    Zum Beispiel: NSApplication + Relaunch.swift:

    extension NSApplication { 
        func relaunch(sender: AnyObject?) { 
         let task = NSTask() 
         // helper tool path 
         task.launchPath = NSBundle.mainBundle().pathForResource("relaunch", ofType: nil)! 
         // self PID as a argument 
         task.arguments = [String(NSProcessInfo.processInfo().processIdentifier)] 
         task.launch() 
        } 
    } 
    

Dann rufen NSApplication.sharedApplication().relaunch(nil) wie Sie möchten.

+0

Dies funktioniert nicht für Swift 2/3. Kannst du es aktualisieren? – Matt

+0

@Matt Siehe meine [Antwort] (http: // stackoverflow.com/a/39591935/597020) –

+0

Ich bin nicht 100% sicher, aber ich konnte die App nicht innerhalb des Relaunch-Tools mit 'app.terminate()' beenden. Es wurde von der ** Sandbox ** abgelehnt. Console.app-Ausgabe: 'AppleEvents/sandbox: Rückgabe von errAEPCrivilegeError/-10004 und Verweigerung des Versands des Ereignisses aevt/quit vom Prozess ''/0x0-0x0, pid = 28376, weil es nicht berechtigt ist, ein AppleEvent an diesen Prozess zu senden Am Ende habe ich die App in der NSApplication-Erweiterung beendet, nachdem ich das Relaunch-Tool gestartet habe. 'NSApp.terminate (nil)' – Daniel

1

Umgehung Rintaro den Code in Swift 2.

// the application pid 
let parentPID = atoi(C_ARGV[1]) 

zu

// the application pid 
let parentPID = Int32(Process.arguments[1]) 

Es funktioniert für mich.

3

Swift 3 Version, basierend auf Rintaros Code und Cenox Kangs Workaround.
Siehe Rintaros Antwort für Anweisungen.

Relaunch/main.swift:

import AppKit 

// KVO helper 
class Observer: NSObject { 

    let _callback:() -> Void 

    init(callback: @escaping() -> Void) { 
     _callback = callback 
    } 

    override func observeValue(forKeyPath keyPath: String?, 
         of object: Any?, 
         change: [NSKeyValueChangeKey : Any]?, 
         context: UnsafeMutableRawPointer?) { 
     _callback() 
    } 
} 


// main 
autoreleasepool { 

    // the application pid 
    guard let parentPID = Int32(CommandLine.arguments[1]) else { 
     fatalError("Relaunch: parentPID == nil.") 
    } 

    // get the application instance 
    if let app = NSRunningApplication(processIdentifier: parentPID) { 

     // application URL 
     let bundleURL = app.bundleURL! 

     // terminate() and wait terminated. 
     let listener = Observer { CFRunLoopStop(CFRunLoopGetCurrent()) } 
     app.addObserver(listener, forKeyPath: "isTerminated", context: nil) 
     app.terminate() 
     CFRunLoopRun() // wait KVO notification 
     app.removeObserver(listener, forKeyPath: "isTerminated", context: nil) 

     // relaunch 
     do { 
      try NSWorkspace.shared().launchApplication(at: bundleURL, configuration: [:]) 
     } catch { 
      fatalError("Relaunch: NSWorkspace.shared().launchApplication failed.") 
     } 
    } 
} 

NSApplication + Relaunch.swift:

import AppKit 

extension NSApplication { 
    func relaunch(sender: AnyObject?) { 
     let task = Process() 
     // helper tool path 
     task.launchPath = Bundle.main.path(forResource: "relaunch", ofType: nil)! 
     // self PID as a argument 
     task.arguments = [String(ProcessInfo.processInfo.processIdentifier)] 
     task.launch() 
    } 
} 
+1

Ich bin mir nicht 100% sicher, aber ich konnte die App nicht innerhalb des Relaunch-Tools mit 'app.terminate()' beenden. Es wurde von der ** Sandbox ** abgelehnt. Console.app-Ausgabe: 'AppleEvents/sandbox: Rückgabe von errAEPCrivilegeError/-10004 und Verweigerung des Versands des Ereignisses aevt/quit vom Prozess ''/0x0-0x0, pid = 28376, weil es nicht berechtigt ist, ein AppleEvent an diesen Prozess zu senden Am Ende habe ich die App in der NSApplication-Erweiterung beendet, nachdem ich das Relaunch-Tool gestartet habe. 'NSApp.terminate (nil)' – Daniel

2

swift4 Funktion

@objc private func buttonClicked(_ sender: NSButton) { 
    if let path = Bundle.main.resourceURL?.deletingLastPathComponent().deletingLastPathComponent().absoluteString { 
     NSLog("restart \(path)") 
     _ = Process.launchedProcess(launchPath: "/usr/bin/open", arguments: [path]) 
     NSApp.terminate(self) 
    } 
} 
+0

Ich denke du solltest dich etwas besser erklären! – IlGala

+1

Code ist klar, ich denke keine Erklärung benötigt hier –