diff --git a/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor DEV.xcscheme b/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor DEV.xcscheme
index 8642ae0..b0a4d86 100644
--- a/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor DEV.xcscheme
+++ b/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor DEV.xcscheme
@@ -91,7 +91,7 @@
+ isEnabled = "YES">
Self {
+ public static func dummy(named service: String, enabled: Bool) -> HomebrewService {
return HomebrewService(
name: service,
service_name: service,
@@ -41,4 +63,10 @@ struct HomebrewService: Decodable, Equatable, Hashable {
hasher.combine(service_name)
hasher.combine(pid)
}
+
+ static func == (lhs: HomebrewService, rhs: HomebrewService) -> Bool {
+ return lhs.name == rhs.name
+ && lhs.pid == rhs.pid
+ && lhs.status == rhs.status
+ }
}
diff --git a/phpmon/Common/Testables/TestableConfiguration.swift b/phpmon/Common/Testables/TestableConfiguration.swift
index 4c05182..beaf040 100644
--- a/phpmon/Common/Testables/TestableConfiguration.swift
+++ b/phpmon/Common/Testables/TestableConfiguration.swift
@@ -26,6 +26,9 @@ public struct TestableConfiguration: Codable {
ActiveCommand.useTestable(commandOutput)
Log.info("Applying fake scanner...")
ValetScanner.useFake()
+ Log.info("Applying fake services manager...")
+ Homebrew.fake = true
+ ServicesManager.useFake()
Log.info("Applying fake Valet domain interactor...")
ValetInteractor.useFake()
}
diff --git a/phpmon/Domain/App/Services/FakeServicesManager.swift b/phpmon/Domain/App/Services/FakeServicesManager.swift
index 53ae7d2..0601028 100644
--- a/phpmon/Domain/App/Services/FakeServicesManager.swift
+++ b/phpmon/Domain/App/Services/FakeServicesManager.swift
@@ -21,13 +21,14 @@ class FakeServicesManager: ServicesManager {
super.init()
Log.warn("A fake services manager is being used, so Homebrew formula resolver is set to act in fake mode.")
- Log.warn("If you do not want this behaviour, never instantiate FakeServicesManager!")
+ Log.warn("If you do not want this behaviour, do not make use of a `FakeServicesManager`!")
self.fixedFormulae = formulae
self.fixedStatus = status
- self.services = self.formulae.map {
+ self.serviceWrappers = self.formulae.map {
let wrapper = ServiceWrapper(formula: $0)
+ wrapper.isBusy = (status == .loading)
wrapper.service = HomebrewService.dummy(named: $0.name, enabled: true)
return wrapper
}
@@ -38,4 +39,16 @@ class FakeServicesManager: ServicesManager {
return HomebrewFormula.init(formula, elevated: false)
}
}
+
+ override func updateServices() async {
+ await delay(seconds: 0.3)
+
+ for formula in self.serviceWrappers {
+ formula.service?.running = true
+ formula.isBusy = false
+ }
+
+ print("Sending the update!")
+ broadcastServicesUpdated()
+ }
}
diff --git a/phpmon/Domain/App/Services/ServicesManager.swift b/phpmon/Domain/App/Services/ServicesManager.swift
index da2fad4..b8d124b 100644
--- a/phpmon/Domain/App/Services/ServicesManager.swift
+++ b/phpmon/Domain/App/Services/ServicesManager.swift
@@ -13,20 +13,31 @@ class ServicesManager: ObservableObject {
@ObservedObject static var shared: ServicesManager = ValetServicesManager()
- @Published var services = [ServiceWrapper]()
+ @Published var serviceWrappers = [ServiceWrapper]()
+ public static func useFake() {
+ ServicesManager.shared = FakeServicesManager.init(
+ formulae: ["php", "nginx", "dnsmasq"],
+ status: .loading
+ )
+ }
+
+ /**
+ The order of services is important, so easy access is accomplished
+ without much fanfare through subscripting.
+ */
subscript(name: String) -> ServiceWrapper? {
- return self.services.first { wrapper in
+ return self.serviceWrappers.first { wrapper in
wrapper.name == name
}
}
public var statusMessage: String {
- if self.services.isEmpty {
+ if self.serviceWrappers.isEmpty {
return "Loading..."
}
- let statuses = self.services[0...2].map { $0.status }
+ let statuses = self.serviceWrappers[0...2].map { $0.status }
if statuses.contains(.loading) {
return "Determining Valet status..."
}
@@ -41,11 +52,11 @@ class ServicesManager: ObservableObject {
}
public var statusColor: Color {
- if self.services.isEmpty {
+ if self.serviceWrappers.isEmpty {
return .yellow
}
- let statuses = self.services[0...2].map { $0.status }
+ let statuses = self.serviceWrappers[0...2].map { $0.status }
if statuses.contains(.loading) {
return .orange
}
@@ -61,14 +72,29 @@ class ServicesManager: ObservableObject {
@available(*, deprecated, message: "Use a more specific method instead")
static func loadHomebrewServices() {
- print(self.shared)
+ // print(self.shared)
print("This method must be updated")
}
- public func updateServices() {
+ public func updateServices() async {
fatalError("Must be implemented in child class")
}
+ public func broadcastServicesUpdated() {
+ Task { @MainActor in
+ self.serviceWrappers.forEach { wrapper in
+ guard let service = wrapper.service else {
+ return
+ }
+
+ Log.perf("\(service.name): \(wrapper.status)")
+ wrapper.objectWillChange.send()
+ }
+
+ self.objectWillChange.send()
+ }
+ }
+
var formulae: [HomebrewFormula] {
var formulae = [
Homebrew.Formulae.php,
@@ -88,7 +114,7 @@ class ServicesManager: ObservableObject {
init() {
Log.info("The services manager will determine which Valet services exist on this system.")
- services = formulae.map {
+ serviceWrappers = formulae.map {
ServiceWrapper(formula: $0)
}
}
diff --git a/phpmon/Domain/App/Services/ValetServicesManager.swift b/phpmon/Domain/App/Services/ValetServicesManager.swift
index 6319169..9ffff96 100644
--- a/phpmon/Domain/App/Services/ValetServicesManager.swift
+++ b/phpmon/Domain/App/Services/ValetServicesManager.swift
@@ -13,10 +13,10 @@ class ValetServicesManager: ServicesManager {
super.init()
// Load the initial services state
- self.updateServices()
+ Task { await self.updateServices() }
}
- override func updateServices() {
+ override func updateServices() async {
// TODO
}
}
diff --git a/phpmon/Domain/Menu/MainMenu+Async.swift b/phpmon/Domain/Menu/MainMenu+Async.swift
index 261f25d..ca9b766 100644
--- a/phpmon/Domain/Menu/MainMenu+Async.swift
+++ b/phpmon/Domain/Menu/MainMenu+Async.swift
@@ -74,7 +74,7 @@ extension MainMenu {
}
if behaviours.contains(.broadcastServicesUpdate) {
- Task { await ServicesManager.loadHomebrewServices() }
+ Task { await ServicesManager.shared.updateServices() }
}
if error != nil {
diff --git a/phpmon/Domain/Menu/MainMenu+Startup.swift b/phpmon/Domain/Menu/MainMenu+Startup.swift
index 2698049..c5a9571 100644
--- a/phpmon/Domain/Menu/MainMenu+Startup.swift
+++ b/phpmon/Domain/Menu/MainMenu+Startup.swift
@@ -96,7 +96,7 @@ extension MainMenu {
Valet.notifyAboutUnsupportedTLD()
// Find out which services are active
- Log.info("The services manager knows about \(ServicesManager.shared.services.count) services.")
+ Log.info("The services manager knows about \(ServicesManager.shared.serviceWrappers.count) services.")
// Start the background refresh timer
startSharedTimer()
diff --git a/phpmon/Domain/Menu/MainMenu.swift b/phpmon/Domain/Menu/MainMenu.swift
index 8ec944e..727e54a 100644
--- a/phpmon/Domain/Menu/MainMenu.swift
+++ b/phpmon/Domain/Menu/MainMenu.swift
@@ -94,7 +94,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
self.refreshActiveInstallation()
self.refreshIcon()
self.rebuild()
- await ServicesManager.loadHomebrewServices()
+ await ServicesManager.shared.updateServices()
Log.perf("The menu has been reloaded!")
}
}
@@ -184,7 +184,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
// Make sure the shortcut key does not trigger this when the menu is open
App.shared.shortcutHotkey?.isPaused = true
Task { // Reload Homebrew services information asynchronously
- await ServicesManager.loadHomebrewServices()
+ await ServicesManager.shared.updateServices()
}
}
diff --git a/phpmon/Domain/SwiftUI/Menu/ServicesView.swift b/phpmon/Domain/SwiftUI/Menu/ServicesView.swift
index 4fd7c23..b1b1320 100644
--- a/phpmon/Domain/SwiftUI/Menu/ServicesView.swift
+++ b/phpmon/Domain/SwiftUI/Menu/ServicesView.swift
@@ -41,14 +41,14 @@ struct ServicesView: View {
init(manager: ServicesManager, perRow: Int, height: CGFloat? = nil) {
self.manager = manager
self.perRow = perRow
- self.chunkCount = manager.services.chunked(by: perRow).count
+ self.chunkCount = manager.serviceWrappers.chunked(by: perRow).count
self.height = CGFloat((50 * chunkCount) + (5 * perRow))
}
var body: some View {
VStack {
VStack(alignment: .leading) {
- ForEach(manager.services.chunked(by: perRow), id: \.self) { chunk in
+ ForEach(manager.serviceWrappers.chunked(by: perRow), id: \.self) { chunk in
HStack {
ForEach(chunk) { service in
ServiceView(service: service).frame(minWidth: 70)
@@ -150,9 +150,16 @@ struct ServicesView_Previews: PreviewProvider {
static var previews: some View {
ServicesView(manager: FakeServicesManager(
formulae: ["php", "nginx", "dnsmasq"],
- status: .active
+ status: .loading
), perRow: 4)
.frame(width: 330.0)
.previewDisplayName("Loading")
+
+ ServicesView(manager: FakeServicesManager(
+ formulae: ["php", "nginx", "dnsmasq"],
+ status: .active
+ ), perRow: 4)
+ .frame(width: 330.0)
+ .previewDisplayName("Active")
}
}