@@ -13,7 +13,7 @@ typealias Runtime = SimCtl.Runtime
13
13
typealias DeviceType = SimCtl . DeviceType
14
14
15
15
/// Stores one simulator and its identifier.
16
- struct Simulator : Identifiable , Comparable , Hashable {
16
+ struct Simulator : Identifiable , Comparable {
17
17
enum State {
18
18
case unknown
19
19
case creating
@@ -37,6 +37,28 @@ struct Simulator: Identifiable, Comparable, Hashable {
37
37
self = . unknown
38
38
}
39
39
}
40
+
41
+ var menuActionName : String {
42
+ switch self {
43
+ case . unknown: " "
44
+ case . creating: " Creating... "
45
+ case . booting: " Booting... "
46
+ case . booted: " Shutdown "
47
+ case . shuttingDown: " Shutting Down... "
48
+ case . shutdown: " Boot "
49
+ }
50
+ }
51
+
52
+ var isActionAllowed : Bool {
53
+ switch self {
54
+ case . unknown: false
55
+ case . creating: false
56
+ case . booting: false
57
+ case . booted: true
58
+ case . shuttingDown: false
59
+ case . shutdown: true
60
+ }
61
+ }
40
62
}
41
63
42
64
/// The user-facing name for this simulator, e.g. iPhone 11 Pro Max.
@@ -64,7 +86,7 @@ struct Simulator: Identifiable, Comparable, Hashable {
64
86
let deviceType : DeviceType ?
65
87
66
88
/// The current state of the simulator
67
- let state : State
89
+ private ( set ) var state : State
68
90
69
91
/// Wheter this simulator is the `Default` one or not
70
92
var isDefault : Bool {
@@ -100,81 +122,91 @@ struct Simulator: Identifiable, Comparable, Hashable {
100
122
self . image = typeIdentifier. icon
101
123
}
102
124
103
- func urlForFilePath( _ filePath: FilePathKind ) -> URL {
104
-
105
- if filePath == . root {
106
- return URL ( fileURLWithPath: dataPath)
107
- }
108
-
109
- let containerPath = dataPath + " /Containers/Shared/AppGroup/ "
110
-
111
- guard let containerContents = try ? FileManager . default. contentsOfDirectory ( atPath: containerPath) else {
112
- print ( " could not find any subfolders in ' \( containerPath) ' " )
113
- return URL ( fileURLWithPath: " " )
114
- }
115
-
116
- for content in containerContents {
117
-
118
- if content. hasSuffix ( " DS_Store " ) { continue }
119
-
120
- let subDirectoryPath = containerPath + content
121
- let plistUrl = URL ( fileURLWithPath: subDirectoryPath)
122
-
123
- guard let subDirectoryContents = try ? FileManager . default. contentsOfDirectory ( atPath: subDirectoryPath) ,
124
- let plistFile = subDirectoryContents. first ( where: { $0. hasSuffix ( " plist " ) } ) ,
125
- let plistData = try ? Data ( contentsOf: plistUrl. appendingPathComponent ( plistFile) ) ,
126
- let plist = try ? PropertyListSerialization . propertyList ( from: plistData, options: [ ] , format: nil ) as? NSDictionary else {
127
- print ( " could not find or decode the plist file in ' \( subDirectoryPath) ' " )
128
- return URL ( fileURLWithPath: " " )
129
- }
130
-
131
- for value in plist. allValues {
132
- if let value = value as? String {
133
- if value. hasSuffix ( filePath. storageType) {
134
- return URL ( fileURLWithPath: subDirectoryPath) . appendingPathComponent ( " File Provider Storage " , isDirectory: true )
135
- }
136
- }
137
- }
138
- }
139
- print ( " could not find folder of type ' \( filePath) ' in ' \( containerPath) ' " )
140
- return URL ( fileURLWithPath: " " )
141
- }
142
-
143
- func copyFilesFromProviders( _ providers: [ NSItemProvider ] , toFilePath filePath: FilePathKind ) -> Bool {
144
- for provider in providers {
145
- provider. loadItem ( forTypeIdentifier: " public.file-url " , options: nil ) { ( urlData, error) in
146
- if let urlData = urlData as? Data {
147
- let sourceUrl = NSURL ( absoluteURLWithDataRepresentation: urlData, relativeTo: nil ) as URL
148
- do {
149
- try FileManager . default. copyItem ( at: sourceUrl, to: urlForFilePath ( filePath) . appendingPathComponent ( sourceUrl. lastPathComponent) )
150
- NSSound ( named: " Glass " ) ? . play ( )
151
- } catch {
152
- NSSound ( named: " Sosumi " ) ? . play ( )
153
- print ( error. localizedDescription)
154
- }
155
- } else {
156
- NSSound ( named: " Sosumi " ) ? . play ( )
157
- }
158
- sleep ( 1 ) // if multiple files are dropped, allow user to distinguish success/error sounds
159
- }
160
- }
161
- return true
162
- }
163
-
164
- enum FilePathKind {
165
- case root, files // photos is complicated, and you can't just drop files there anyway
166
-
167
- var storageType : String {
168
- switch self {
169
- case . root:
170
- print ( " Storage type is not applicable to the root path " )
171
- return " "
172
- case . files:
173
- return " LocalStorage "
174
- }
175
- }
176
- }
125
+ mutating func update( state: State ) {
126
+ self . state = state
127
+ }
128
+
129
+ func open( _ filePath: FilePathKind ) {
130
+ NSWorkspace . shared. activateFileViewerSelecting ( [ urlForFilePath ( filePath) ] )
131
+ }
132
+
133
+ func urlForFilePath( _ filePath: FilePathKind ) -> URL {
134
+
135
+ if filePath == . root {
136
+ return URL ( fileURLWithPath: dataPath)
137
+ }
138
+
139
+ let containerPath = dataPath + " /Containers/Shared/AppGroup/ "
140
+
141
+ guard let containerContents = try ? FileManager . default. contentsOfDirectory ( atPath: containerPath) else {
142
+ print ( " could not find any subfolders in ' \( containerPath) ' " )
143
+ return URL ( fileURLWithPath: " " )
144
+ }
145
+
146
+ for content in containerContents {
147
+
148
+ if content. hasSuffix ( " DS_Store " ) { continue }
149
+
150
+ let subDirectoryPath = containerPath + content
151
+ let plistUrl = URL ( fileURLWithPath: subDirectoryPath)
152
+
153
+ guard let subDirectoryContents = try ? FileManager . default. contentsOfDirectory ( atPath: subDirectoryPath) ,
154
+ let plistFile = subDirectoryContents. first ( where: { $0. hasSuffix ( " plist " ) } ) ,
155
+ let plistData = try ? Data ( contentsOf: plistUrl. appendingPathComponent ( plistFile) ) ,
156
+ let plist = try ? PropertyListSerialization . propertyList ( from: plistData, options: [ ] , format: nil ) as? NSDictionary else {
157
+ print ( " could not find or decode the plist file in ' \( subDirectoryPath) ' " )
158
+ return URL ( fileURLWithPath: " " )
159
+ }
160
+
161
+ for value in plist. allValues {
162
+ if let value = value as? String {
163
+ if value. hasSuffix ( filePath. storageType) {
164
+ return URL ( fileURLWithPath: subDirectoryPath) . appendingPathComponent ( " File Provider Storage " , isDirectory: true )
165
+ }
166
+ }
167
+ }
168
+ }
169
+ print ( " could not find folder of type ' \( filePath) ' in ' \( containerPath) ' " )
170
+ return URL ( fileURLWithPath: " " )
171
+ }
172
+
173
+ func copyFilesFromProviders( _ providers: [ NSItemProvider ] , toFilePath filePath: FilePathKind ) -> Bool {
174
+ for provider in providers {
175
+ provider. loadItem ( forTypeIdentifier: " public.file-url " , options: nil ) { ( urlData, error) in
176
+ if let urlData = urlData as? Data {
177
+ let sourceUrl = NSURL ( absoluteURLWithDataRepresentation: urlData, relativeTo: nil ) as URL
178
+ do {
179
+ try FileManager . default. copyItem ( at: sourceUrl, to: urlForFilePath ( filePath) . appendingPathComponent ( sourceUrl. lastPathComponent) )
180
+ NSSound ( named: " Glass " ) ? . play ( )
181
+ } catch {
182
+ NSSound ( named: " Sosumi " ) ? . play ( )
183
+ print ( error. localizedDescription)
184
+ }
185
+ } else {
186
+ NSSound ( named: " Sosumi " ) ? . play ( )
187
+ }
188
+ sleep ( 1 ) // if multiple files are dropped, allow user to distinguish success/error sounds
189
+ }
190
+ }
191
+ return true
192
+ }
193
+
194
+ enum FilePathKind {
195
+ case root, files // photos is complicated, and you can't just drop files there anyway
177
196
197
+ var storageType : String {
198
+ switch self {
199
+ case . root:
200
+ print ( " Storage type is not applicable to the root path " )
201
+ return " "
202
+ case . files:
203
+ return " LocalStorage "
204
+ }
205
+ }
206
+ }
207
+ }
208
+
209
+ extension Simulator : Hashable {
178
210
/// Sort simulators alphabetically, and then by OS version.
179
211
static func < ( lhs: Simulator , rhs: Simulator ) -> Bool {
180
212
if lhs. name == rhs. name,
@@ -184,7 +216,10 @@ struct Simulator: Identifiable, Comparable, Hashable {
184
216
}
185
217
return lhs. name < rhs. name
186
218
}
219
+ }
187
220
221
+ /// Preview
222
+ extension Simulator {
188
223
/// An example simulator for Xcode preview purposes
189
224
static let example = Simulator ( name: " iPhone 11 Pro max " , udid: UUID ( ) . uuidString, state: . booted, runtime: . unknown, deviceType: nil , dataPath: " <example data path> " )
190
225
0 commit comments