Skip to content

Commit b904ddb

Browse files
authored
Merge branch 'okwasniewski:main' into main
2 parents 308a84b + cdc6827 commit b904ddb

File tree

4 files changed

+107
-68
lines changed

4 files changed

+107
-68
lines changed

MiniSim/AppleScript Commands/LaunchDeviceCommand.swift

+1-9
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,11 @@ class LaunchDeviceCommand: NSScriptCommand {
3030
return nil
3131
}
3232

33-
DispatchQueue.global(qos: .userInitiated).async {
34-
if device.platform == .android {
35-
try? DeviceService.launchDevice(name: device.name)
36-
} else {
37-
try? DeviceService.launchDevice(uuid: device.ID ?? "")
38-
}
39-
}
40-
33+
DeviceService.launch(device: device) { _ in }
4134
return nil
4235
} catch {
4336
scriptErrorNumber = NSInternalScriptError;
4437
return nil
4538
}
46-
4739
}
4840
}

MiniSim/Menu.swift

+21-39
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,16 @@ class Menu: NSMenu {
3434
}
3535

3636
func getDevices() {
37-
DispatchQueue.global(qos: .userInitiated).async {
38-
do {
39-
var devicesArray: [Device] = []
40-
if UserDefaults.standard.enableiOSSimulators {
41-
try devicesArray.append(contentsOf: DeviceService.getIOSDevices())
42-
}
43-
if UserDefaults.standard.enableAndroidEmulators && UserDefaults.standard.androidHome != nil {
44-
try devicesArray.append(contentsOf: DeviceService.getAndroidDevices())
45-
}
46-
self.devices = devicesArray
47-
} catch {
37+
let userDefaults = UserDefaults.standard
38+
DeviceService.getAllDevices(
39+
android: userDefaults.enableAndroidEmulators && userDefaults.androidHome != nil,
40+
iOS: userDefaults.enableiOSSimulators
41+
) { devices, error in
42+
if let error {
4843
NSAlert.showError(message: error.localizedDescription)
44+
return
4945
}
46+
self.devices = devices
5047
}
5148
}
5249

@@ -75,22 +72,14 @@ class Menu: NSMenu {
7572

7673
@objc private func deviceItemClick(_ sender: NSMenuItem) {
7774
guard let device = getDeviceByName(name: sender.title) else { return }
78-
guard let tag = DeviceMenuItem(rawValue: sender.tag) else { return }
7975

8076
if device.booted {
8177
DeviceService.focusDevice(device)
8278
return
8379
}
8480

85-
DispatchQueue.global(qos: .userInitiated).async {
86-
do {
87-
switch tag {
88-
case .launchAndroid:
89-
try DeviceService.launchDevice(name: device.name)
90-
case .launchIOS:
91-
try DeviceService.launchDevice(uuid: device.ID ?? "")
92-
}
93-
} catch {
81+
DeviceService.launch(device: device) { error in
82+
if let error {
9483
NSAlert.showError(message: error.localizedDescription)
9584
}
9685
}
@@ -116,9 +105,7 @@ class Menu: NSMenu {
116105
private func assignKeyEquivalent(devices: [NSMenuItem]) {
117106
for (index, item) in devices.enumerated() {
118107
if index > maxKeyEquivalent {
119-
DispatchQueue.main.async {
120-
item.keyEquivalent = ""
121-
}
108+
item.keyEquivalent = ""
122109
continue
123110
}
124111

@@ -128,25 +115,23 @@ class Menu: NSMenu {
128115
continue
129116
}
130117

131-
DispatchQueue.main.async {
132-
if self.items.contains(item) {
133-
item.keyEquivalent = keyEquivalent
134-
}
118+
if self.items.contains(item) {
119+
item.keyEquivalent = keyEquivalent
135120
}
136121
}
137122
}
138-
123+
139124

140125
private func populateDevices(isFirst: Bool) {
141126
let sortedDevices = devices.sorted(by: { $0.platform == .android && $1.platform == .ios })
142127
for (index, device) in sortedDevices.enumerated() {
143128
let isAndroid = device.platform == .android
144129
if let itemIndex = items.firstIndex(where: { $0.title == device.displayName }) {
145-
DispatchQueue.main.async { [self] in
146-
let item = self.items.get(at: itemIndex)
147-
item?.state = device.booted ? .on : .off
148-
item?.submenu = isAndroid ? populateAndroidSubMenu(booted: device.booted) : populateIOSSubMenu(booted: device.booted)
149-
}
130+
let item = self.items.get(at: itemIndex)
131+
item?.state = device.booted ? .on : .off
132+
item?.submenu = isAndroid ?
133+
self.populateAndroidSubMenu(booted: device.booted) :
134+
self.populateIOSSubMenu(booted: device.booted)
150135
continue
151136
}
152137

@@ -162,11 +147,8 @@ class Menu: NSMenu {
162147
menuItem.submenu = isAndroid ? populateAndroidSubMenu(booted: device.booted) : populateIOSSubMenu(booted: device.booted)
163148
menuItem.state = device.booted ? .on : .off
164149

165-
DispatchQueue.main.async {
166-
let iosDevicesCount = self.devices.filter({ $0.platform == .ios }).count
167-
self.safeInsertItem(menuItem, at: isAndroid && UserDefaults.standard.enableiOSSimulators ? (isFirst ? index : iosDevicesCount) + 3 : 1)
168-
}
169-
150+
let iosDevicesCount = self.devices.filter({ $0.platform == .ios }).count
151+
self.safeInsertItem(menuItem, at: isAndroid && UserDefaults.standard.enableiOSSimulators ? (isFirst ? index : iosDevicesCount) + 3 : 1)
170152
}
171153
}
172154

MiniSim/MiniSim.swift

+7-8
Original file line numberDiff line numberDiff line change
@@ -160,19 +160,18 @@ class MiniSim: NSObject {
160160
if !shouldDelete {
161161
return
162162
}
163-
DispatchQueue.global(qos: .userInitiated).async {
164-
do {
165-
let amountCleared = try DeviceService.clearDerivedData()
166-
UNUserNotificationCenter.showNotification(title: "Derived data has been cleared!", body: "Removed \(amountCleared) of data")
167-
NotificationCenter.default.post(name: .commandDidSucceed, object: nil)
168-
} catch {
169-
NSAlert.showError(message: error.localizedDescription)
163+
164+
DeviceService.clearDerivedData() { amountCleared, error in
165+
guard error == nil else {
166+
NSAlert.showError(message: error?.localizedDescription ?? "Failed to clear derived data.")
167+
return
170168
}
169+
UNUserNotificationCenter.showNotification(title: "Derived data has been cleared!", body: "Removed \(amountCleared) of data")
170+
NotificationCenter.default.post(name: .commandDidSucceed, object: nil)
171171
}
172172
default:
173173
break
174174
}
175-
176175
}
177176
}
178177

MiniSim/Service/DeviceService.swift

+78-12
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,11 @@ import AppKit
1111
import UserNotifications
1212

1313
protocol DeviceServiceProtocol {
14-
static func launchDevice(uuid: String) throws
1514
static func getIOSDevices() throws -> [Device]
1615
static func checkXcodeSetup() -> Bool
1716
static func deleteSimulator(uuid: String) throws
18-
static func clearDerivedData() throws -> String
1917
static func handleiOSAction(device: Device, commandTag: IOSSubMenuItem, itemName: String)
2018

21-
static func launchDevice(name: String, additionalArguments: [String]) throws
2219
static func toggleA11y(device: Device) throws
2320
static func getAndroidDevices() throws -> [Device]
2421
static func sendText(device: Device, text: String) throws
@@ -34,6 +31,7 @@ protocol DeviceServiceProtocol {
3431

3532
class DeviceService: DeviceServiceProtocol {
3633

34+
private static let queue = DispatchQueue(label: "com.MiniSim.DeviceService", qos: .userInteractive)
3735
private static let deviceBootedError = "Unable to boot device in current state: Booted"
3836

3937
private static let derivedDataLocation = "~/Library/Developer/Xcode/DerivedData"
@@ -93,7 +91,7 @@ class DeviceService: DeviceServiceProtocol {
9391
}
9492

9593
static func focusDevice(_ device: Device) {
96-
DispatchQueue.global(qos: .userInitiated).async {
94+
queue.async {
9795

9896
let runningApps = NSWorkspace.shared.runningApplications.filter({$0.activationPolicy == .regular})
9997

@@ -155,6 +153,63 @@ class DeviceService: DeviceServiceProtocol {
155153
UNUserNotificationCenter.showNotification(title: title, body: message)
156154
NotificationCenter.default.post(name: .commandDidSucceed, object: nil)
157155
}
156+
157+
static func getAllDevices(
158+
android: Bool,
159+
iOS: Bool,
160+
completionQueue: DispatchQueue = .main,
161+
completion: @escaping ([Device], Error?) -> ()
162+
) {
163+
queue.async {
164+
do {
165+
var devicesArray: [Device] = []
166+
167+
if android {
168+
try devicesArray.append(contentsOf: getAndroidDevices())
169+
}
170+
171+
if iOS {
172+
try devicesArray.append(contentsOf: getIOSDevices())
173+
}
174+
175+
completionQueue.async {
176+
completion(devicesArray, nil)
177+
}
178+
} catch {
179+
completionQueue.async {
180+
completion([], error)
181+
}
182+
}
183+
}
184+
}
185+
186+
private static func launch(device: Device) throws {
187+
switch device.platform {
188+
case .ios:
189+
try launchDevice(uuid: device.ID ?? "")
190+
case .android:
191+
try launchDevice(name: device.name)
192+
}
193+
}
194+
195+
static func launch(device: Device, completionQueue: DispatchQueue = .main, completion: @escaping (Error?) -> Void) {
196+
self.queue.async {
197+
do {
198+
try self.launch(device: device)
199+
completionQueue.async {
200+
completion(nil)
201+
}
202+
}
203+
catch {
204+
guard error.localizedDescription.contains(deviceBootedError) else {
205+
return
206+
}
207+
completionQueue.async {
208+
completion(error)
209+
}
210+
}
211+
}
212+
}
158213
}
159214

160215
// MARK: iOS Methods
@@ -182,10 +237,21 @@ extension DeviceService {
182237
return devices
183238
}
184239

185-
static func clearDerivedData() throws -> String {
186-
let amountCleared = try? shellOut(to: "du -sh \(derivedDataLocation)").match(###"\d+\.?\d+\w+"###).first?.first
187-
try shellOut(to: "rm -rf \(derivedDataLocation)")
188-
return amountCleared ?? ""
240+
static func clearDerivedData(completionQueue: DispatchQueue = .main, completion: @escaping (String, Error?) -> Void) {
241+
self.queue.async {
242+
do {
243+
let amountCleared = try? shellOut(to: "du -sh \(derivedDataLocation)").match(###"\d+\.?\d+\w+"###).first?.first
244+
try shellOut(to: "rm -rf \(derivedDataLocation)")
245+
completionQueue.async {
246+
completion(amountCleared ?? "", nil)
247+
}
248+
}
249+
catch {
250+
completionQueue.async {
251+
completion("", error)
252+
}
253+
}
254+
}
189255
}
190256

191257
static func getIOSDevices() throws -> [Device] {
@@ -206,7 +272,7 @@ extension DeviceService {
206272
}
207273
}
208274

209-
static func launchDevice(uuid: String) throws {
275+
private static func launchDevice(uuid: String) throws {
210276
do {
211277
try self.launchSimulatorApp(uuid: uuid)
212278
try shellOut(to: ProcessPaths.xcrun.rawValue, arguments: ["simctl", "boot", uuid])
@@ -237,7 +303,7 @@ extension DeviceService {
237303
if !NSAlert.showQuestionDialog(title: "Are you sure?", message: "Are you sure you want to delete this Simulator?") {
238304
return
239305
}
240-
DispatchQueue.global(qos: .userInitiated).async {
306+
queue.async {
241307
do {
242308
try DeviceService.deleteSimulator(uuid: deviceID)
243309
DeviceService.showSuccessMessage(title: "Simulator deleted!", message: deviceID)
@@ -250,7 +316,7 @@ extension DeviceService {
250316
guard let command = DeviceService.getCustomCommand(platform: .ios, commandName: itemName) else {
251317
return
252318
}
253-
DispatchQueue.global(qos: .userInitiated).async {
319+
queue.async {
254320
do {
255321
try DeviceService.runCustomCommand(device, command: command)
256322
} catch {
@@ -267,7 +333,7 @@ extension DeviceService {
267333

268334
// MARK: Android Methods
269335
extension DeviceService {
270-
static func launchDevice(name: String, additionalArguments: [String] = []) throws {
336+
private static func launchDevice(name: String, additionalArguments: [String] = []) throws {
271337
let emulatorPath = try ADB.getEmulatorPath()
272338
var arguments = ["@\(name)"]
273339
let formattedArguments = additionalArguments.filter({ !$0.isEmpty }).map {

0 commit comments

Comments
 (0)