<% additional_paths = Enum.map_join(@app_options[:additional_paths], ":", fn path -> if String.starts_with?(path, "/") do path else "\\(resourcePath)/#{path}" end end) %>import Cocoa class AppDelegate: NSObject, NSApplicationDelegate { var releaseTask: Process! var isRunning = false var initialInput = "open_app" func applicationDidFinishLaunching(_ aNotification: Notification) { if !isRunning { releaseTask = startRelease(initialInput) isRunning = true } } func applicationWillTerminate(_ n: Notification) { if (releaseTask.isRunning == true) { log("terminating release task") releaseTask.terminate() } } func application(_ app: NSApplication, open urls: [URL]) { for url in urls { var input : String if url.isFileURL { input = "open_file:\(url.path)" } else { input = "open_url:\(url)" } if isRunning { rpc(input) } else { initialInput = input } } } } func startRelease(_ input : String) -> Process { let task = buildReleaseTask() task.environment!["APP_BUILDER_INPUT"] = input task.arguments = ["start"] task.terminationHandler = {(t: Process) in if t.terminationStatus == 0 { log("release exited with: \(t.terminationStatus)") } else { runAlert(messageText: "\(appName) exited with error status \(t.terminationStatus).") } NSApp.terminate(nil) } try! task.run() log("release pid: \(task.processIdentifier)") DispatchQueue.global(qos: .userInteractive).async { task.waitUntilExit() } return task } func rpc(_ event: String) { let input = Pipe() let task = buildReleaseTask() task.standardInput = input input.fileHandleForWriting.write("\(event)\n".data(using: .utf8)!) task.arguments = ["rpc", "AppBundler.__rpc__()"] try! task.run() task.waitUntilExit() if task.terminationStatus != 0 { runAlert(messageText: "Something went wrong") } } func buildReleaseTask() -> Process { let task = Process() task.launchPath = Bundle.main.path(forResource: "rel/bin/<%= @release.name %>", ofType: "")! task.environment = ProcessInfo.processInfo.environment <%= if additional_paths != "" do %> let resourcePath = Bundle.main.resourcePath ?? "" _ = resourcePath let additionalPaths = "<%= additional_paths %>" let path = task.environment!["PATH"] ?? "" task.environment!["PATH"] = "\(additionalPaths):\(path)" <% end %> task.standardOutput = logFile task.standardError = logFile return task } func runAlert(messageText: String) { DispatchQueue.main.sync { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = messageText alert.informativeText = "Logs available at: \(logPath)" alert.runModal() } } func log(_ line: String) { logFile.write("[\(appName)Launcher] \(line)\n".data(using: .utf8)!) } let fm = FileManager.default let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as! String let home = NSHomeDirectory() let logPath = "\(home)/Library/Logs/\(appName).log" if !fm.fileExists(atPath: logPath) { fm.createFile(atPath: logPath, contents: Data()) } let logFile = FileHandle(forUpdatingAtPath: logPath)! logFile.seekToEndOfFile() let app = NSApplication.shared let delegate = AppDelegate() app.delegate = delegate app.run()