-
Notifications
You must be signed in to change notification settings - Fork 30
Expand file tree
/
Copy pathSimulator+Device.swift
More file actions
151 lines (130 loc) · 3.09 KB
/
Simulator+Device.swift
File metadata and controls
151 lines (130 loc) · 3.09 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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//
// Simulator+Device.swift
// AppleDeviceKit
//
// Created by Lukas Romsicki on 2022-10-20.
// Copyright © 2022 Shopify. All rights reserved.
//
import Foundation
import TophatFoundation
import RegexBuilder
import ShellKit
extension Simulator: Device {
var id: String {
udid
}
var runtime: Runtime {
.init(from: runtimeIdentifier)
}
var type: DeviceType {
.simulator
}
var connection: Connection {
.internal
}
var state: DeviceState {
DeviceState(from: rawState)
}
var isLocked: Bool {
// Simulators do not lock.
false
}
func boot() async throws {
do {
try SimCtl.start(udid: id)
try focus()
} catch {
throw DiagnosticError(DeviceError.failedToBoot, technicalDetails: error.shellErrorDiagnosticMessage)
}
}
func focus() throws {
do {
try run(command: .open(.simulator), log: log)
} catch {
throw DeviceError.failedToFocus
}
}
func install(application: Application) throws {
do {
// Failure to terminate is permitted as the application may not be running.
try? SimCtl.terminate(udid: id, bundleIdentifier: application.bundleIdentifier)
try SimCtl.install(udid: id, bundleUrl: application.url)
} catch {
throw DiagnosticError(DeviceError.failedToInstallApp(bundleUrl: application.url, deviceType: .simulator), technicalDetails: error.shellErrorDiagnosticMessage)
}
}
func launch(application: Application, arguments: [String]? = nil) throws {
let bundleIdentifier = try application.bundleIdentifier
do {
try SimCtl.launch(udid: id, bundleIdentifier: bundleIdentifier, arguments: arguments ?? [])
} catch {
throw DiagnosticError(DeviceError.failedToLaunchApp(bundleId: bundleIdentifier, reason: .unexpected, deviceType: .simulator), technicalDetails: error.shellErrorDiagnosticMessage)
}
}
func waitUntilUnlocked() async throws {
// Simulators do not lock.
}
}
private extension Runtime {
nonisolated(unsafe) private static let search = Regex {
"com.apple.CoreSimulator.SimRuntime."
TryCapture {
ChoiceOf {
"iOS"
"watchOS"
"tvOS"
"visionOS"
}
} transform: { substring in
String(substring)
}
"-"
TryCapture {
OneOrMore(.any)
} transform: { substring in
substring.replacingOccurrences(of: "-", with: ".")
}
}
init(from runtimeIdentifier: String) {
guard let (_, platform, version) = runtimeIdentifier.firstMatch(of: Self.search)?.output else {
self.init(platform: .unknown, version: .unknown)
return
}
self.init(platform: .init(from: platform), version: .init(from: version))
}
}
private extension Platform {
init(from string: String?) {
switch string {
case "iOS":
self = .iOS
case "watchOS":
self = .watchOS
case "tvOS":
self = .tvOS
case "visionOS":
self = .visionOS
default:
self = .unknown
}
}
}
private extension RuntimeVersion {
init(from string: String?) {
if let string = string {
self = .exact(string)
return
}
self = .unknown
}
}
private extension DeviceState {
init(from simulatorState: Simulator.State) {
switch simulatorState {
case .booted:
self = .ready
case .shutdown:
self = .unavailable
}
}
}