Files
NetNewsWire/AppKitExtras/Sources/AppKitExtras/UserApp.swift

141 lines
3.2 KiB
Swift

//
// UserApp.swift
// RSCore
//
// Created by Brent Simmons on 1/14/18.
// Copyright © 2018 Ranchero Software, LLC. All rights reserved.
//
#if os(macOS)
import AppKit
/// Represents an app (the type of app mostly found in /Applications.)
///
/// The app may or may not be running. It may or may not exist.
public final class UserApp {
public let bundleID: String
public var icon: NSImage? = nil
public var existsOnDisk = false
public var path: String? = nil
public var runningApplication: NSRunningApplication? = nil
public var isRunning: Bool {
updateStatus()
if let runningApplication = runningApplication {
return !runningApplication.isTerminated
}
return false
}
public init(bundleID: String) {
self.bundleID = bundleID
updateStatus()
}
public func updateStatus() {
if let runningApplication = runningApplication, runningApplication.isTerminated {
self.runningApplication = nil
}
let runningApplications = NSRunningApplication.runningApplications(withBundleIdentifier: bundleID)
for app in runningApplications {
if let runningApplication = runningApplication {
if app == runningApplication {
break
}
}
else {
if !app.isTerminated {
runningApplication = app
break
}
}
}
if let runningApplication = runningApplication {
existsOnDisk = true
icon = runningApplication.icon
if let bundleURL = runningApplication.bundleURL {
path = bundleURL.path
}
else {
path = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: bundleID)
}
if icon == nil, let path = path {
icon = NSWorkspace.shared.icon(forFile: path)
}
return
}
path = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: bundleID)
if let path = path {
if icon == nil {
icon = NSWorkspace.shared.icon(forFile: path)
}
existsOnDisk = true
}
else {
existsOnDisk = false
icon = nil
}
}
public func launchIfNeeded() -> Bool {
// Return true if already running.
// Return true if not running and successfully gets launched.
updateStatus()
if isRunning {
return true
}
guard existsOnDisk, let path = path else {
return false
}
let url = URL(fileURLWithPath: path)
if let app = try? NSWorkspace.shared.launchApplication(at: url, options: [.withErrorPresentation], configuration: [:]) {
runningApplication = app
if app.isFinishedLaunching {
return true
}
Thread.sleep(forTimeInterval: 1.0) // Give the app time to launch. This is ugly.
if app.isFinishedLaunching {
return true
}
Thread.sleep(forTimeInterval: 1.0) // Give it some *more* time.
return true
}
return false
}
public func bringToFront() -> Bool {
// Activates the app, ignoring other apps.
// Does not automatically launch the app first.
updateStatus()
return runningApplication?.activate() ?? false
}
public func targetDescriptor() -> NSAppleEventDescriptor? {
// Requires that the app has previously been launched.
updateStatus()
guard let runningApplication = runningApplication, !runningApplication.isTerminated else {
return nil
}
return NSAppleEventDescriptor(runningApplication: runningApplication)
}
}
#endif