Add UserApp class to RSCore. It represents an of the type usually found in /Applications. A UserApp may or may not be running and may or may not exist locally on disk. It could be entirely fictional, even.

This commit is contained in:
Brent Simmons 2018-01-14 10:56:06 -08:00
parent 8df34bfcda
commit fd7c6d07ac
2 changed files with 127 additions and 0 deletions

View File

@ -71,6 +71,7 @@
842E45CC1ED623C7000A8B52 /* UniqueIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842E45CB1ED623C7000A8B52 /* UniqueIdentifier.swift */; };
8432B1861DACA0E90057D6DF /* NSResponder-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8432B1851DACA0E90057D6DF /* NSResponder-Extensions.swift */; };
8432B1881DACA2060057D6DF /* NSWindow-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8432B1871DACA2060057D6DF /* NSWindow-Extensions.swift */; };
8434D15C200BD6F400D6281E /* UserApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8434D15B200BD6F400D6281E /* UserApp.swift */; };
84411E731FE5FFC3004B527F /* NSImage+RSCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84411E721FE5FFC3004B527F /* NSImage+RSCore.swift */; };
844B5B571FE9D36000C7C76A /* Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844B5B561FE9D36000C7C76A /* Keyboard.swift */; };
844C915B1B65753E0051FC1B /* RSPlist.h in Headers */ = {isa = PBXBuildFile; fileRef = 844C91591B65753E0051FC1B /* RSPlist.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -191,6 +192,7 @@
842E45CB1ED623C7000A8B52 /* UniqueIdentifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UniqueIdentifier.swift; sourceTree = "<group>"; };
8432B1851DACA0E90057D6DF /* NSResponder-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSResponder-Extensions.swift"; sourceTree = "<group>"; };
8432B1871DACA2060057D6DF /* NSWindow-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSWindow-Extensions.swift"; sourceTree = "<group>"; };
8434D15B200BD6F400D6281E /* UserApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = UserApp.swift; path = AppKit/UserApp.swift; sourceTree = "<group>"; };
84411E721FE5FFC3004B527F /* NSImage+RSCore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "NSImage+RSCore.swift"; path = "Images/NSImage+RSCore.swift"; sourceTree = "<group>"; };
844B5B561FE9D36000C7C76A /* Keyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Keyboard.swift; path = RSCore/Keyboard.swift; sourceTree = "<group>"; };
844C91591B65753E0051FC1B /* RSPlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RSPlist.h; path = RSCore/RSPlist.h; sourceTree = "<group>"; };
@ -460,6 +462,7 @@
84C687311FBAA3DF00345C9E /* LogWindowController.swift */,
84C687341FBC025600345C9E /* Log.swift */,
84C687371FBC028900345C9E /* LogItem.swift */,
8434D15B200BD6F400D6281E /* UserApp.swift */,
842DD7F91E1499FA00E061EB /* Views */,
);
name = AppKit;
@ -794,6 +797,7 @@
842E45CC1ED623C7000A8B52 /* UniqueIdentifier.swift in Sources */,
84A8358A1D4EC7B80004C598 /* PlistProviderProtocol.swift in Sources */,
849A339E1AC90A0A0015BA09 /* NSTableView+RSCore.m in Sources */,
8434D15C200BD6F400D6281E /* UserApp.swift in Sources */,
84CFF51B1AC3C77500CEA6C8 /* RSPlatform.m in Sources */,
845A291F1FC8BC49007B49E3 /* BinaryDiskCache.swift in Sources */,
84CFF52C1AC3CA9700CEA6C8 /* NSData+RSCore.m in Sources */,

View File

@ -0,0 +1,123 @@
//
// UserApp.swift
// RSCore
//
// Created by Brent Simmons on 1/14/18.
// Copyright © 2018 Ranchero Software, LLC. All rights reserved.
//
import Cocoa
// 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.
final class UserApp {
let bundleID: String
var icon: NSImage? = nil
var existsOnDisk = false
var path: String? = nil
var runningApplication: NSRunningApplication? = nil
var isRunning: Bool {
updateStatus()
if let runningApplication = runningApplication {
return !runningApplication.isTerminated
}
return false
}
init(bundleID: String) {
self.bundleID = bundleID
updateStatus()
}
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
}
}
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: 0.5) // Give the app time to launch. This is ugly.
return true
}
return false
}
func bringToFront() -> Bool {
// Activates the app, ignoring other apps.
// Does not automatically launch the app first.
updateStatus()
return runningApplication?.activate(options: [.activateIgnoringOtherApps]) ?? false
}
}