How to parse xcrun simctl devices

Issue #559

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
public class GetDestinations {
public init() {}

public func getAvailable(workflow: Workflow) throws -> [Destination] {
let processHandler = DefaultProcessHandler(filter: { $0.starts(with: "name=") })
let string = try CommandLine().runBash(
workflow: workflow,
program: "xcrun simctl",
arguments: [
"list",
"devices",
"-j"
],
processHandler: processHandler
)

guard let data = string.data(using: .utf8) else {
throw PumaError.invalid
}

let response: Response = try JSONDecoder().decode(Response.self, from: data)
let devicesWithOS: [DeviceWithOS] = response.devices.flatMap({ key, value in
return value.map({ DeviceWithOS(device: $0, os: key) })
})

let destinations: [Destination] = try devicesWithOS
.filter({ withOS in
return withOS.device.isAvailable
})
.compactMap({ withOS in
guard
let platform = self.platform(withOS: withOS),
let os = try self.os(withOS: withOS)
else {
return nil
}

var destination = Destination(
name: withOS.device.name,
platform: platform,
os: os
)
destination.id = withOS.device.udid
return destination
})

return destinations
}

func findId(workflow: Workflow, destination: Destination) throws -> String? {
let availableDestinations = try self.getAvailable(workflow: workflow)
return availableDestinations.first(where: { $0 == destination })?.id
}

// MARK: - Private

private func platform(withOS: DeviceWithOS) -> String? {
let list: [String] = [
Destination.Platform.iOS,
Destination.Platform.watchOS,
Destination.Platform.macOS,
Destination.Platform.tvOS,
]

return list.first(where: { withOS.os.contains($0) })
}

// com.apple.CoreSimulator.SimRuntime.iOS-13-2
private func os(withOS: DeviceWithOS) throws -> String? {
guard let string = try withOS.os.matches(pattern: #"(-\d+)+"#).first else {
return nil
}

return string.dropFirst().replacingOccurrences(of: "-", with: ".")
}
}

private struct Response: Decodable {
let devices: [String: [Device]]
}

private struct Device: Decodable {
let state: String
let name: String
let udid: String
let isAvailable: Bool
}

private struct DeviceWithOS {
let device: Device
let os: String
}

Comments