-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathActiveApplicationMonitor.swift
More file actions
115 lines (100 loc) · 3.6 KB
/
ActiveApplicationMonitor.swift
File metadata and controls
115 lines (100 loc) · 3.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import AppKit
public struct RunningApplicationInfo: Sendable {
public let isXcode: Bool
public let isActive: Bool
public let isHidden: Bool
public let localizedName: String?
public let bundleIdentifier: String?
public let bundleURL: URL?
public let executableURL: URL?
public let processIdentifier: pid_t
public let launchDate: Date?
public let executableArchitecture: Int
init(_ application: NSRunningApplication) {
isXcode = application.isXcode
isActive = application.isActive
isHidden = application.isHidden
localizedName = application.localizedName
bundleIdentifier = application.bundleIdentifier
bundleURL = application.bundleURL
executableURL = application.executableURL
processIdentifier = application.processIdentifier
launchDate = application.launchDate
executableArchitecture = application.executableArchitecture
}
}
public extension NSRunningApplication {
var info: RunningApplicationInfo { RunningApplicationInfo(self) }
}
public final class ActiveApplicationMonitor {
public static let shared = ActiveApplicationMonitor()
public private(set) var latestXcode: NSRunningApplication? = NSWorkspace.shared
.runningApplications
.first(where: \.isXcode)
public private(set) var previousApp: NSRunningApplication?
public private(set) var activeApplication = NSWorkspace.shared.runningApplications
.first(where: \.isActive)
{
didSet {
if activeApplication?.isXcode ?? false {
latestXcode = activeApplication
}
previousApp = oldValue
}
}
private var infoContinuations: [UUID: AsyncStream<RunningApplicationInfo?>.Continuation] = [:]
private init() {
activeApplication = NSWorkspace.shared.runningApplications.first(where: \.isActive)
Task {
let sequence = NSWorkspace.shared.notificationCenter
.notifications(named: NSWorkspace.didActivateApplicationNotification)
for await notification in sequence {
guard let app = notification
.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication
else { continue }
activeApplication = app
notifyContinuations()
}
}
}
deinit {
for continuation in infoContinuations {
continuation.value.finish()
}
}
public var activeXcode: NSRunningApplication? {
if activeApplication?.isXcode ?? false {
return activeApplication
}
return nil
}
public func createInfoStream() -> AsyncStream<RunningApplicationInfo?> {
.init { continuation in
let id = UUID()
Task { @MainActor in
continuation.onTermination = { _ in
self.removeInfoContinuation(id: id)
}
addInfoContinuation(continuation, id: id)
continuation.yield(activeApplication?.info)
}
}
}
func addInfoContinuation(
_ continuation: AsyncStream<RunningApplicationInfo?>.Continuation,
id: UUID
) {
infoContinuations[id] = continuation
}
func removeInfoContinuation(id: UUID) {
infoContinuations[id] = nil
}
private func notifyContinuations() {
for continuation in infoContinuations {
continuation.value.yield(activeApplication?.info)
}
}
}
public extension NSRunningApplication {
var isXcode: Bool { bundleIdentifier == "com.apple.dt.Xcode" }
}