Added ability to boot / shutdown devices

This commit is contained in:
Casey Fleser 2022-07-11 21:13:18 -05:00
parent 0e5b2fbf78
commit e75373084a
3 changed files with 67 additions and 3 deletions

View file

@ -43,7 +43,34 @@ struct SimCtl {
return try JSONDecoder().decode([String : [String : [SimDevice]]].self, from: json)["devices"] ?? [:]
}
func readDevice(_ device: SimDevice) throws -> SimDevice? {
let json : Data = try run(args: ["list", "-j", "devices", device.udid])
let decoded = try JSONDecoder().decode([String : [String : [SimDevice]]].self, from: json)["devices"] ?? [:]
var result : SimDevice? = nil
for devices in decoded.values {
if let match = devices.first(where: { $0.udid == device.udid }) {
result = match
break
}
}
return result
}
func readAllDevices() throws -> [SimDevice] {
return try readAllRuntimeDevices().flatMap { $1 }
}
func bootDevice(_ device: SimDevice, boot: Bool) async throws {
try await withThrowingTaskGroup(of: Void.self) { group in
group.addTask { let _ : Data = try run(args: [boot ? "boot" : "shutdown", device.udid]) }
group.addTask { try await Task.sleep(nanoseconds: 1_000_000_000) }
try await group.next() // delay or run complate
}
if let refreshedDev = try readDevice(device) {
await MainActor.run { () -> Void in device.updateDevice(from: refreshedDev) }
}
}
}

View file

@ -25,12 +25,24 @@ class SimDevice: ObservableObject, Decodable {
case booted = "Booted"
case shuttingDown = "Shutting Down"
case shutdown = "Shutdown"
var showBooted : Bool {
switch self {
case .booted, .booting: return true
default: return false
}
}
}
@Published var name : String
@Published var state : State
@Published var isAvailable : Bool
@Published var availabilityError : String?
var isTransitioning : Bool { state == .booting || state == .shuttingDown }
var isBooted : Bool {
get { state.showBooted == true }
set { bootDevice(newValue) }
}
let udid : String
let dataPath : String
@ -98,7 +110,7 @@ class SimDevice: ObservableObject, Decodable {
return !(name == other.name && state == other.state && isAvailable == other.isAvailable && availabilityError == other.availabilityError)
}
func updateDevice(from other: SimDevice) -> Bool {
@discardableResult func updateDevice(from other: SimDevice) -> Bool {
guard hasChanged(from: other) else { return false }
name = other.name
@ -108,6 +120,21 @@ class SimDevice: ObservableObject, Decodable {
return true
}
func bootDevice(_ boot: Bool) {
if boot && state == .shutdown || !boot && state == .booted {
let simctl = SimCtl()
state = boot ? .booting : .shuttingDown
Task {
do {
try await simctl.bootDevice(self, boot: boot)
} catch {
print("Failed to \(boot ? "boot" : "shutdown") device: \(error)")
}
}
}
}
}
extension SimDevice: SourceItemData {

View file

@ -12,11 +12,21 @@ extension SimDevice {
}
struct DeviceHeader: View {
@ObservedObject var device : SimDevice
@ObservedObject var device : SimDevice
var body: some View {
VStack(alignment: .leading, spacing: 3.0) {
Text("State: \(device.state.rawValue)")
HStack(spacing: 8.0) {
Toggle("Booted", isOn: $device.isBooted)
.toggleStyle(.switch)
.disabled(device.isTransitioning)
if device.isTransitioning {
ProgressView()
.controlSize(.small)
Text(device.state.rawValue)
}
}
Text("UDID: \(device.udid)")
}
.font(.subheadline)