xcsim 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +37 -0
- data/bin/xcsim +252 -0
- data/lib/xcsim/rbAppBundles.rb +124 -0
- data/lib/xcsim/rbBundleInfo.rb +142 -0
- data/lib/xcsim/rbConstants.rb +28 -0
- data/lib/xcsim/rbDeviceID.rb +78 -0
- data/lib/xcsim/rbDeviceSet.rb +67 -0
- data/lib/xcsim/rbList.rb +148 -0
- data/lib/xcsim/rbOSDevices.rb +47 -0
- data/lib/xcsim/rbOSID.rb +89 -0
- data/lib/xcsim/rbReports.rb +42 -0
- data/lib/xcsim/rbXCSim.rb +121 -0
- data/lib/xcsim.rb +13 -0
- metadata +73 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7001c7d48bafa2526204efa6fe004587f6881890
|
4
|
+
data.tar.gz: 364f09686a392b1d893f3c3dcde0a3ea12be1974
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1409c1748ae77efb92f9d8ce41fdd7b12b9a6569eb3880428e406ab9b46354e0d41ca34ffaefc76192e18ea69cda43e26aea67ec092cedad688a8df1a96e8b41
|
7
|
+
data.tar.gz: 188424f9644888129ecaea27b6c6c497885bf458af282558f9f98f3422c9649fd880f42b3f76869d6afe69f962e19c9b1c3dcfd70baae06b83273b2c162a59f3
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Egor Chiglintsev.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# xcsim
|
2
|
+
|
3
|
+
xcsim is a command-line utility to simplify opening iOS Simulator application directories in Finder.
|
4
|
+
iOS Simulator uses UUIDs heavily for naming its directories, which makes them not really readable
|
5
|
+
by human eye. Consider for example:
|
6
|
+
|
7
|
+
/Users/User/Library/Developer/CoreSimulator/Devices/FC138577-990F-4069-9AB1-6D847B8529BD/data/
|
8
|
+
Containers/Data/Application/E34BF950-B343-4C73-B50F-EEEA0E34BFAC
|
9
|
+
|
10
|
+
Suppose you have an iOS application with bundle ID `com.yourcompany.appname` and want to peek
|
11
|
+
into its Documents folder, which is stored in iPhone 5s (iOS 9.2) simulator. Finding the right
|
12
|
+
directory could be a tedious task. With xcsim it is as easy as
|
13
|
+
|
14
|
+
xcsim --os "iOS 9.2" --device "iPhone 5s" com.yourcompany.appname
|
15
|
+
|
16
|
+
or even as just
|
17
|
+
|
18
|
+
xcsim com.yourcompany.appname
|
19
|
+
|
20
|
+
if it happens that default OS and device values are enough for your needs.
|
21
|
+
|
22
|
+
In case you're not sure which exact simulator your app is installed on, you can use xcsim to list
|
23
|
+
all present simulators by OS type, device name and view bundle IDs of the applications installed
|
24
|
+
on them.
|
25
|
+
|
26
|
+
By default xcsim invokes the `open` command to open the application data directory in Finder. By
|
27
|
+
providing the `--app` option you can open application bundle directory instead. Using `--echo`
|
28
|
+
option will print the full path to the selected directory instead of opening Finder.
|
29
|
+
|
30
|
+
Usage: `xcsim [options] [bundleID]`
|
31
|
+
|
32
|
+
-l, --list [OS], [DEVICE] List simulator OS, devices, app bundle IDs
|
33
|
+
-o, --os 'TYPE VERSION' Select simulator OS (default: 'iOS 9.2')
|
34
|
+
-d, --device 'TYPE' Select simulator device (default: 'iPhone 5s')
|
35
|
+
-a, --app Select application container directory instead of data directory
|
36
|
+
-e, --echo Echo the selected directory instead of opening it
|
37
|
+
-h, --help [OPTION] Print this message / help for individual options
|
data/bin/xcsim
ADDED
@@ -0,0 +1,252 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'cfpropertylist'
|
6
|
+
rescue LoadError
|
7
|
+
puts "require 'cfpropertylist' failed! Consider running 'gem install CFPropertyList'"
|
8
|
+
exit
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'xcsim'
|
12
|
+
|
13
|
+
def banner
|
14
|
+
<<-END_BANNER
|
15
|
+
xcsim is a command-line utility to simplify opening iOS Simulator application directories in Finder.
|
16
|
+
iOS Simulator uses UUIDs heavily for naming its directories, which makes them not really readable
|
17
|
+
by human eye. Consider for example:
|
18
|
+
|
19
|
+
/Users/User/Library/Developer/CoreSimulator/Devices/FC138577-990F-4069-9AB1-6D847B8529BD/data/
|
20
|
+
Containers/Data/Application/E34BF950-B343-4C73-B50F-EEEA0E34BFAC
|
21
|
+
|
22
|
+
Suppose you have an iOS application with bundle ID 'com.yourcompany.appname' and want to peek
|
23
|
+
into its Documents folder, which is stored in iPhone 5s (iOS 9.2) simulator. Finding the right
|
24
|
+
directory could be a tedious task. With xcsim it is as easy as
|
25
|
+
|
26
|
+
xcsim --os "iOS 9.2" --device "iPhone 5s" com.yourcompany.appname
|
27
|
+
|
28
|
+
or even as just
|
29
|
+
|
30
|
+
xcsim com.yourcompany.appname
|
31
|
+
|
32
|
+
if it happens that default OS and device values are enough for your needs.
|
33
|
+
|
34
|
+
In case you're not sure which exact simulator your app is installed on, you can use xcsim to list
|
35
|
+
all present simulators by OS type, device name and view bundle IDs of the applications installed
|
36
|
+
on them.
|
37
|
+
|
38
|
+
By default xcsim invokes the `open` command to open the application data directory in Finder. By
|
39
|
+
providing the `--app` option you can open application bundle directory instead. Using `--echo`
|
40
|
+
option will print the full path to the selected directory instead of opening Finder.
|
41
|
+
|
42
|
+
Usage: xcsim [options] [bundleID]
|
43
|
+
|
44
|
+
END_BANNER
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
def help_list
|
50
|
+
<<-END_LIST_HELP
|
51
|
+
Usage: xcsim --list [OS], [DEVICE]
|
52
|
+
|
53
|
+
List option allows listing available simulators/OS versions or bundle IDs of installed apps.
|
54
|
+
Actual output of the list option depends on the parameters provided. Both parameters are optional,
|
55
|
+
and if both present, should be separated by a comma.
|
56
|
+
|
57
|
+
Without parameters `xcsim --list` prints all iOS simulator OS versions available with respective
|
58
|
+
count of installed devices for each of the OS:
|
59
|
+
|
60
|
+
xcsim --list
|
61
|
+
|
62
|
+
iOS 8.0 (6 devices)
|
63
|
+
iOS 8.3 (5 devices)
|
64
|
+
iOS 9.0 (6 devices)
|
65
|
+
iOS 9.1 (4 devices)
|
66
|
+
iOS 9.2 (10 devices)
|
67
|
+
|
68
|
+
Both OS and DEVICE parameters can be used to partially match existing simulators and print the
|
69
|
+
matching results. Considering the example above, we could receive the following outputs:
|
70
|
+
|
71
|
+
xcsim --list 9 # note partial match on OS version
|
72
|
+
|
73
|
+
iOS 9.0 (6 devices)
|
74
|
+
iOS 9.1 (4 devices)
|
75
|
+
iOS 9.2 (10 devices)
|
76
|
+
|
77
|
+
xcsim --list 9.1 # exact match yields list of devices for the given OS version
|
78
|
+
|
79
|
+
iPhone 4s
|
80
|
+
iPhone 5s
|
81
|
+
iPhone 6 Plus
|
82
|
+
iPhone 6s Plus
|
83
|
+
|
84
|
+
xcsim --list iPad # partial match on device type, lists devices with OS versions
|
85
|
+
|
86
|
+
iPad 2 (iOS 8.0)
|
87
|
+
iPad Air (iOS 8.0)
|
88
|
+
iPad Air (iOS 8.3)
|
89
|
+
iPad Air (iOS 9.0)
|
90
|
+
iPad 2 (iOS 9.2)
|
91
|
+
iPad Air (iOS 9.2)
|
92
|
+
iPad Air 2 (iOS 9.2)
|
93
|
+
|
94
|
+
xcsim --list iOS 9.2, iPad Air 2 # exact device match lists bundle IDs of installed apps
|
95
|
+
|
96
|
+
com.yourcompany.helloapp
|
97
|
+
com.yourcompany.otherapp
|
98
|
+
END_LIST_HELP
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
def help_os
|
103
|
+
<<-END_OS_HELP
|
104
|
+
Usage: xcsim --os "iOS 9.2" com.yourcompany.appname
|
105
|
+
|
106
|
+
Sets the OS version to work with when searching for bundle ID (needs to be an exact match, i.e.
|
107
|
+
partial matches such as "iOS" or "iOS 9" are not allowed)
|
108
|
+
|
109
|
+
If omitted, a default OS will be used instead (#{XCSim::defaultOSName})
|
110
|
+
END_OS_HELP
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
def help_device
|
115
|
+
<<-END_DEVICE_HELP
|
116
|
+
Usage: xcsim --device "iPhone 5s" com.yourcompany.appname
|
117
|
+
|
118
|
+
Sets the device type to work with when searching for bundle ID (needs to be an exact match, i.e.
|
119
|
+
partial matches such as "iPhone" are not allowed)
|
120
|
+
|
121
|
+
If omitted, a default device will be used instead (#{XCSim::defaultDeviceName})
|
122
|
+
END_DEVICE_HELP
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def help_app
|
127
|
+
<<-END_APP_HELP
|
128
|
+
Usage: xcsim --app com.yourcompany.appname
|
129
|
+
|
130
|
+
Selects application bundle directory instead of application data directory (which is default).
|
131
|
+
END_APP_HELP
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
def help_echo
|
136
|
+
<<-END_ECHO_HELP
|
137
|
+
Usage: xcsim --echo com.yourcompany.appname
|
138
|
+
|
139
|
+
Prints the selected directory path instead of opening it.
|
140
|
+
END_ECHO_HELP
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
parser = OptionParser.new do |opts|
|
145
|
+
opts.banner = banner
|
146
|
+
|
147
|
+
opts.on_tail("-h", "--help [OPTION]", "Print this message / help for individual options") do |v|
|
148
|
+
if v == "l" || v == "list"
|
149
|
+
puts help_list
|
150
|
+
elsif v == "o" || v == "os"
|
151
|
+
puts help_os
|
152
|
+
elsif v == "d" || v == "device"
|
153
|
+
puts help_device
|
154
|
+
elsif v == "a" || v == "app"
|
155
|
+
puts help_app
|
156
|
+
elsif v == "e" || v == "echo"
|
157
|
+
puts help_echo
|
158
|
+
else
|
159
|
+
puts opts
|
160
|
+
end
|
161
|
+
exit
|
162
|
+
end
|
163
|
+
|
164
|
+
opts.on("-l", "--list [OS], [DEVICE]",
|
165
|
+
"List simulator OS, devices, app bundle IDs") do |v|
|
166
|
+
@command = :list
|
167
|
+
@argument = ([v] + ARGV).join(" ")
|
168
|
+
end
|
169
|
+
|
170
|
+
opts.on("-o", "--os 'TYPE VERSION'",
|
171
|
+
"Select simulator OS (default: '#{XCSim::defaultOSName}')") do |v|
|
172
|
+
@argument ||= {}
|
173
|
+
@argument[:os] = v
|
174
|
+
end
|
175
|
+
|
176
|
+
opts.on("-d", "--device 'TYPE'",
|
177
|
+
"Select simulator device (default: '#{XCSim::defaultDeviceName}')") do |v|
|
178
|
+
@argument ||= {}
|
179
|
+
@argument[:device] = v
|
180
|
+
end
|
181
|
+
|
182
|
+
opts.on("-a", "--app", "Select application container directory instead of data directory") do |v|
|
183
|
+
@argument ||= {}
|
184
|
+
@argument[:dir] = :app
|
185
|
+
end
|
186
|
+
|
187
|
+
opts.on("-e", "--echo", "Echo the selected directory instead of opening it") do |v|
|
188
|
+
@argument ||= {}
|
189
|
+
@argument[:output] = :echo
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
parser.parse!
|
194
|
+
|
195
|
+
begin
|
196
|
+
if @command == nil
|
197
|
+
if ARGV.count > 1
|
198
|
+
raise ArgumentError
|
199
|
+
end
|
200
|
+
|
201
|
+
@command ||= :bundle
|
202
|
+
@argument ||= {}
|
203
|
+
|
204
|
+
@argument[:bundleID] ||= ARGV.first
|
205
|
+
end
|
206
|
+
|
207
|
+
result = xcsim(@command, @argument)
|
208
|
+
|
209
|
+
case @command
|
210
|
+
when :list
|
211
|
+
unless result.empty?
|
212
|
+
puts XCSim::reportFromDeviceList(result)
|
213
|
+
else
|
214
|
+
puts "ERROR: Could not find simulators matching '#{@argument}'!\n"
|
215
|
+
puts parser
|
216
|
+
exit 1
|
217
|
+
end
|
218
|
+
|
219
|
+
when :bundle
|
220
|
+
path = (@argument[:dir] == :app) ? result.bundlePath : result.dataPath
|
221
|
+
|
222
|
+
if @argument[:output] == :echo
|
223
|
+
puts path
|
224
|
+
else
|
225
|
+
`open #{path}`
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
exit
|
230
|
+
|
231
|
+
rescue XCSim::NonUniqueBundleIDError => error
|
232
|
+
puts "ERROR: Multiple data directories matching bundle ID '#{error.bundleID}' on #{error.deviceID} " +
|
233
|
+
"found:"
|
234
|
+
puts error.directories
|
235
|
+
exit 1
|
236
|
+
|
237
|
+
rescue XCSim::OSNotFoundError => error
|
238
|
+
puts "ERROR: Unknown OS '#{error.name}'"
|
239
|
+
exit 1
|
240
|
+
|
241
|
+
rescue XCSim::DeviceNotFoundError => error
|
242
|
+
puts "ERROR: Unknown device '#{error.name}' for OS '#{error.os.id}'"
|
243
|
+
exit 1
|
244
|
+
|
245
|
+
rescue XCSim::BundleNotFoundError => error
|
246
|
+
puts "Unknown bundle ID '#{error.bundleID}' on #{error.device.name} (#{error.os.id})"
|
247
|
+
exit 1
|
248
|
+
|
249
|
+
rescue ArgumentError
|
250
|
+
puts parser
|
251
|
+
exit
|
252
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'cfpropertylist'
|
4
|
+
require_relative 'rbBundleInfo'
|
5
|
+
|
6
|
+
module XCSim
|
7
|
+
|
8
|
+
# An error raised by XCSim::findBundleDataPath function when multiple directories matching
|
9
|
+
# the given bundle ID are found on the same simulator device.
|
10
|
+
class NonUniqueBundleIDError < RuntimeError
|
11
|
+
|
12
|
+
# A DeviceID object representing the iOS Simulator on which the error has occurred
|
13
|
+
attr_reader :deviceID
|
14
|
+
|
15
|
+
# A bundle ID string, which caused the error
|
16
|
+
attr_reader :bundleID
|
17
|
+
|
18
|
+
# An array of strings containing absolute paths for directories matching the given bundle ID
|
19
|
+
attr_reader :directories
|
20
|
+
|
21
|
+
# Initializes a NonUniqueBundleIDError object with the given parameters
|
22
|
+
def initialize(deviceID, bundleID, directories)
|
23
|
+
@bundleID = bundleID
|
24
|
+
@deviceID = deviceID
|
25
|
+
@directories = directories
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
# call-seq:
|
32
|
+
# findBundleDataPath(deviceID, bundleID) => String
|
33
|
+
#
|
34
|
+
# +deviceID+:: A DeviceID object representing the device simulator on which the application
|
35
|
+
# is installed
|
36
|
+
# +bundleID+:: Bundle ID of the application to search for
|
37
|
+
#
|
38
|
+
# Finds an absolute path for application data directory of an application with a given bundle ID
|
39
|
+
# installed on a given device.
|
40
|
+
#
|
41
|
+
# Searches for directories matching +bundleID+ inside the application data directory of the
|
42
|
+
# device. Raises a NonUniqueBundleIDError if multiple matching directories
|
43
|
+
# are found (bundle IDs are expected to be unique). Returns the found directory or +nil+
|
44
|
+
# otherwise.
|
45
|
+
def self.findBundleDataPath(deviceID, bundleID)
|
46
|
+
path = deviceID.appDataPath
|
47
|
+
subdirs = Dir.entries(path).select do |entry|
|
48
|
+
File.directory? File.join(path, entry) and !(entry =='.' || entry == '..')
|
49
|
+
end
|
50
|
+
|
51
|
+
metadataPairs = subdirs.map do |dir|
|
52
|
+
metadataPath = "#{path}/#{dir}/#{BUNDLE_METADATA_PLIST}"
|
53
|
+
|
54
|
+
if File.exists? metadataPath
|
55
|
+
plist = CFPropertyList::List.new(:file => metadataPath)
|
56
|
+
plist = CFPropertyList.native_types(plist.value)
|
57
|
+
|
58
|
+
{ :plist => plist, :dir => "#{path}/#{dir}" }
|
59
|
+
else
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
.select{ |pair| pair != nil }
|
64
|
+
|
65
|
+
result = metadataPairs.select{ |pair| pair[:plist][METADATA_ID] == bundleID }
|
66
|
+
|
67
|
+
if result.count > 1
|
68
|
+
raise NonUniqueBundleIDError.new(deviceID, bundleID, result.map{|pair| pair[:dir]})
|
69
|
+
elsif result.empty?
|
70
|
+
return nil
|
71
|
+
else
|
72
|
+
result.first[:dir]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
# call-seq:
|
79
|
+
# parseInstalledBundles(deviceID) => Hash
|
80
|
+
#
|
81
|
+
# +deviceID+:: A DeviceID object to search installed app bundles for
|
82
|
+
#
|
83
|
+
# Searches the given iOS Simulator device for installed application bundles (excluding the
|
84
|
+
# system applications such as Safari) and returns a Hash of <tt>bundle ID string =>
|
85
|
+
# BundleInfo</tt> containing BundleInfo instances corresponding the found bundles.
|
86
|
+
#
|
87
|
+
# May raise a NonUniqueBundleIDError if multiple data directories are found for one
|
88
|
+
# of the bundles.
|
89
|
+
def self.parseInstalledBundles(deviceID)
|
90
|
+
path = deviceID.appBundlesPath
|
91
|
+
subdirs = Dir.entries(path).select do |entry|
|
92
|
+
File.directory? File.join(path, entry) and !(entry =='.' || entry == '..')
|
93
|
+
end
|
94
|
+
|
95
|
+
bundlePlists = subdirs.map do |dir|
|
96
|
+
plistPath = "#{path}/#{dir}/#{BUNDLE_METADATA_PLIST}"
|
97
|
+
|
98
|
+
if File.exists? plistPath
|
99
|
+
plist = CFPropertyList::List.new(:file => plistPath)
|
100
|
+
plist = CFPropertyList.native_types(plist.value)
|
101
|
+
|
102
|
+
{ :plist => plist, :dir => "#{path}/#{dir}" }
|
103
|
+
else
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
end
|
107
|
+
.select { |plist| plist != nil }
|
108
|
+
|
109
|
+
bundleInfos = bundlePlists.map do |pair|
|
110
|
+
bundleID = pair[:plist][METADATA_ID]
|
111
|
+
bundlePath = pair[:dir]
|
112
|
+
dataPath = findBundleDataPath(deviceID, bundleID)
|
113
|
+
BundleInfo.new(bundleID, bundlePath, dataPath)
|
114
|
+
end
|
115
|
+
|
116
|
+
bundleInfosHash = {}
|
117
|
+
bundleInfos.each{ |info| bundleInfosHash[info.bundleID] = info }
|
118
|
+
|
119
|
+
bundleInfosHash
|
120
|
+
end
|
121
|
+
|
122
|
+
end # module XCSim
|
123
|
+
|
124
|
+
# eof
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module XCSim
|
4
|
+
# An error, which is raised by GetBundle class when iOS Simulator OS version specified by
|
5
|
+
# the +:os+ option could not be found.
|
6
|
+
class OSNotFoundError < RuntimeError
|
7
|
+
|
8
|
+
# Name of the OS provided in +:os+ option
|
9
|
+
attr_reader :name
|
10
|
+
|
11
|
+
# Initializes an OSNotFoundError instance with the given OS name
|
12
|
+
def initialize(name)
|
13
|
+
@name = name
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
# An error, which is raised by GetBundle class when iOS Simulator device model specified by
|
20
|
+
# the +:device+ option could not be found.
|
21
|
+
class DeviceNotFoundError < RuntimeError
|
22
|
+
|
23
|
+
# An OSDevices object corresponding to the OS version, which was used when searching
|
24
|
+
# for the device
|
25
|
+
attr_reader :os
|
26
|
+
|
27
|
+
# Device name specified in the +:device+ option
|
28
|
+
attr_reader :name
|
29
|
+
|
30
|
+
# Initializes a DeviceNotFoundError instance with a given os and name
|
31
|
+
def initialize(os, name)
|
32
|
+
@os = os
|
33
|
+
@name = name
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
# An error, which is raised by GetBundle class when bundle ID specified by +:bundleID+ option
|
40
|
+
# has not been found (even with partial match) on the device
|
41
|
+
class BundleNotFoundError < RuntimeError
|
42
|
+
|
43
|
+
# An OSDevices object corresponding to the OS version, which was used when searching
|
44
|
+
# for the application bundle
|
45
|
+
attr_reader :os
|
46
|
+
|
47
|
+
# A DeviceID object corresponding to the device, which was used when searching for the
|
48
|
+
# application bundle
|
49
|
+
attr_reader :device
|
50
|
+
|
51
|
+
# Bundle ID provided in +:bundleID+ option
|
52
|
+
attr_reader :bundleID
|
53
|
+
|
54
|
+
# Initializes a BundleNotFoundError instance with a given os, device and bundleID
|
55
|
+
def initialize(os, device, bundleID)
|
56
|
+
@os = os
|
57
|
+
@device = device
|
58
|
+
@bundleID = bundleID
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
# Contains information about a certain application bundle
|
65
|
+
class BundleInfo
|
66
|
+
|
67
|
+
# Bundle ID of the application
|
68
|
+
attr_reader :bundleID
|
69
|
+
|
70
|
+
# Absolute path for the application bundle directory
|
71
|
+
attr_reader :bundlePath
|
72
|
+
|
73
|
+
# Absolute path for the application data directory
|
74
|
+
attr_reader :dataPath
|
75
|
+
|
76
|
+
# Initializes a BundleInfo instance with a given +bundleID+, +bundlePath+, +dataPath+
|
77
|
+
def initialize(bundleID, bundlePath, dataPath)
|
78
|
+
@bundleID = bundleID
|
79
|
+
@bundlePath = bundlePath
|
80
|
+
@dataPath = dataPath
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns +bundleID+
|
84
|
+
def inspect
|
85
|
+
@bundleID
|
86
|
+
end
|
87
|
+
|
88
|
+
# Same as #inspect
|
89
|
+
def to_s
|
90
|
+
inspect
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
# A class encapsulating logic of searching for an applciation bundle in #xcsim function
|
97
|
+
# (i.e. implementation of #xcsim bundle mode)
|
98
|
+
class GetBundle
|
99
|
+
# Initializes a GetBundle instance with a given device set of OSDevices type
|
100
|
+
def initialize(deviceSet)
|
101
|
+
@deviceSet = deviceSet
|
102
|
+
end
|
103
|
+
|
104
|
+
# Performs search for the application bundle matching the provided options.
|
105
|
+
# See #xcsim description (Bundle Mode section) for more info.
|
106
|
+
#
|
107
|
+
# Returns a BundleInfo object
|
108
|
+
def withOptions(options)
|
109
|
+
bundleID = options[:bundleID]
|
110
|
+
|
111
|
+
if bundleID == nil
|
112
|
+
raise ArgumentError
|
113
|
+
end
|
114
|
+
|
115
|
+
os = osNamed(options[:os] || XCSim::defaultOSName)
|
116
|
+
device = deviceNamed(os, options[:device] || XCSim::defaultDeviceName)
|
117
|
+
bundles = XCSim::parseInstalledBundles(device).values
|
118
|
+
|
119
|
+
matchingBundles = bundles.select { |bundle| bundle.bundleID.end_with? bundleID }
|
120
|
+
|
121
|
+
if matchingBundles.count > 1
|
122
|
+
raise NonUniqueBundleIDError.new(device, bundleID, matchingBundles.map { |b| b.bundleID })
|
123
|
+
elsif matchingBundles.empty?
|
124
|
+
raise BundleNotFoundError.new(os, device, bundleID)
|
125
|
+
else
|
126
|
+
return matchingBundles.first
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
def osNamed(name)
|
132
|
+
@deviceSet[OSID.fromString(name)] || (raise OSNotFoundError.new(name))
|
133
|
+
end
|
134
|
+
|
135
|
+
def deviceNamed(os, name)
|
136
|
+
os.devices[name] || (raise DeviceNotFoundError.new(os, name))
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
end # module XCSim
|
141
|
+
|
142
|
+
# eof
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
# XCSim module contains utility for parsing iOS Simulator metadata plist files
|
4
|
+
# and providing access to the bundle and data directories of the applications
|
5
|
+
# installed on iOS Simulator.
|
6
|
+
module XCSim
|
7
|
+
|
8
|
+
# Absolute path of the directory, which stores all of thr iOS Simulator data
|
9
|
+
SIMULATORS_ROOT = File.expand_path("~/Library/Developer/CoreSimulator/Devices")
|
10
|
+
|
11
|
+
# Path for the application bundles directory relative to a concrete iOS Simulator device dir
|
12
|
+
DEVICE_APP_BUNDLES_RELATIVE_PATH = "data/Containers/Bundle/Application"
|
13
|
+
|
14
|
+
# Path for the application data directory relative to a concrete iOS Simulator device dir
|
15
|
+
DEVICE_APP_DATA_RELATIVE_PATH = "data/Containers/Data/Application"
|
16
|
+
|
17
|
+
# Name of the +device_set.plist+ file
|
18
|
+
DEVICE_SET_PLIST = "device_set.plist"
|
19
|
+
|
20
|
+
# Name of the plist file containing a certain application's metadata
|
21
|
+
BUNDLE_METADATA_PLIST = ".com.apple.mobile_container_manager.metadata.plist"
|
22
|
+
|
23
|
+
# A key in application metadata plist file, which corresponds to the bundle ID of the application
|
24
|
+
METADATA_ID = "MCMMetadataIdentifier"
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
# eof
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require_relative 'rbConstants'
|
4
|
+
|
5
|
+
module XCSim
|
6
|
+
|
7
|
+
# DeviceID is a pair of device name string and a +GUID+, which identifies
|
8
|
+
# the concrete iOS Simulator in the file system. DeviceID provides access
|
9
|
+
# to the application bundle and application documents paths.
|
10
|
+
class DeviceID
|
11
|
+
|
12
|
+
# Name of the device (e.g. <tt>'iPhone 5s'</tt>)
|
13
|
+
attr_reader :name
|
14
|
+
|
15
|
+
# Creates a DeviceID instance from a string prefixed by a standard
|
16
|
+
# prefix encountered in +device_set.plist+ (+com.apple.CoreSimulator.SimDeviceType.+)
|
17
|
+
#
|
18
|
+
# Does not perform any additional validation other than checking the prefix.
|
19
|
+
def self.fromPrefixedString(string, guid)
|
20
|
+
unless string.start_with? @@PREFIX
|
21
|
+
return nil
|
22
|
+
end
|
23
|
+
|
24
|
+
name = string.sub(@@PREFIX, "").gsub("-", " ")
|
25
|
+
return DeviceID.new(name, guid)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Initializes a DeviceID instance with a given name and +GUID+.
|
29
|
+
def initialize(name, guid)
|
30
|
+
@name = name
|
31
|
+
@guid = guid
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns an absolute path for application bundles directory
|
35
|
+
# corresponding to the given device.
|
36
|
+
def appBundlesPath
|
37
|
+
"#{SIMULATORS_ROOT}/#{@guid}/#{DEVICE_APP_BUNDLES_RELATIVE_PATH}"
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns an absolute path for application data directory
|
41
|
+
# corresponding to the given device
|
42
|
+
def appDataPath
|
43
|
+
"#{SIMULATORS_ROOT}/#{@guid}/#{DEVICE_APP_DATA_RELATIVE_PATH}"
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns device name
|
47
|
+
def inspect
|
48
|
+
@name
|
49
|
+
end
|
50
|
+
|
51
|
+
# Same as #inspect
|
52
|
+
def to_s
|
53
|
+
inspect
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns a string used for indexing device definitions in +device_set.plist+
|
57
|
+
# in +com.apple.CoreSimulator.SimDeviceType.iPad-2+ format.
|
58
|
+
def key
|
59
|
+
"#{@@PREFIX}#{@name.gsub(" ","-")}"
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns +inspect.hash+
|
63
|
+
def hash
|
64
|
+
inspect.hash
|
65
|
+
end
|
66
|
+
|
67
|
+
# Checks equality by <tt>@name</tt>
|
68
|
+
def eql?(other)
|
69
|
+
@name.eql?(other.name)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
@@PREFIX = "com.apple.CoreSimulator.SimDeviceType."
|
74
|
+
end # class DeviceID
|
75
|
+
|
76
|
+
end # module XCSim
|
77
|
+
|
78
|
+
# eof
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'cfpropertylist'
|
4
|
+
|
5
|
+
require_relative 'rbOSID'
|
6
|
+
require_relative 'rbDeviceID'
|
7
|
+
require_relative 'rbOSDevices'
|
8
|
+
|
9
|
+
module XCSim
|
10
|
+
|
11
|
+
# call-seq:
|
12
|
+
# parseDeviceSet(absolutePath) => Hash
|
13
|
+
#
|
14
|
+
# Parses a +device_set.plist+ file located at the given +path+
|
15
|
+
#
|
16
|
+
# Returns a Hash of <tt>OSID => OSDevices</tt> for iOS Simulator OSes,
|
17
|
+
# which have at least one device simulator installed.
|
18
|
+
def self.parseDeviceSet(path)
|
19
|
+
plist = CFPropertyList::List.new(:file => path)
|
20
|
+
plist = CFPropertyList.native_types(plist.value)
|
21
|
+
defaultDevices = plist["DefaultDevices"]
|
22
|
+
|
23
|
+
osIDs = defaultDevices
|
24
|
+
.keys
|
25
|
+
.map{|s| OSID.fromPrefixedString(s) }
|
26
|
+
.select{|id| id != nil}
|
27
|
+
|
28
|
+
oses = osIDs.map do |id|
|
29
|
+
osDevices = defaultDevices[id.key]
|
30
|
+
devices = osDevices
|
31
|
+
.keys
|
32
|
+
.map{ |s| DeviceID.fromPrefixedString(s, osDevices[s])}
|
33
|
+
.select{ |device| device != nil }
|
34
|
+
.select{ |device| File.directory? device.appBundlesPath }
|
35
|
+
|
36
|
+
(devices.count > 0) ? OSDevices.new(id, devices) : nil
|
37
|
+
end
|
38
|
+
.select{ |os| os != nil }
|
39
|
+
|
40
|
+
osHash = {}
|
41
|
+
oses.each{ |os| osHash[os.id] = os }
|
42
|
+
|
43
|
+
osHash
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns default device set of OSDevices class (as parsed by #parseDeviceSet with default
|
47
|
+
# iOS Simulators path)
|
48
|
+
def self.deviceSet
|
49
|
+
@@deviceSet
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns default OS name for use in #xcsim bundle mode when no +:os+ option is provided.
|
53
|
+
# Selects the OS with the highest version number as the default.
|
54
|
+
def self.defaultOSName
|
55
|
+
@@deviceSet.keys.max.to_s
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns the default device name for use in #xcsim bundle mode when no +:device+
|
59
|
+
# option is provided.
|
60
|
+
def self.defaultDeviceName
|
61
|
+
"iPhone 5s"
|
62
|
+
end
|
63
|
+
|
64
|
+
@@deviceSet = parseDeviceSet("#{XCSim::SIMULATORS_ROOT}/#{XCSim::DEVICE_SET_PLIST}")
|
65
|
+
end # module XCSim
|
66
|
+
|
67
|
+
# eof
|
data/lib/xcsim/rbList.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require_relative 'rbAppBundles'
|
4
|
+
|
5
|
+
module XCSim
|
6
|
+
# An aggregate object containing information on a single iOS Simulator device
|
7
|
+
# returned by GetDeviceList#withPattern or GetDeviceList#allDevices
|
8
|
+
class DeviceListItem
|
9
|
+
|
10
|
+
# An OSDevices object corresponding to the device OS version
|
11
|
+
attr_reader :os
|
12
|
+
|
13
|
+
# A DeviceID object corresponding to the device
|
14
|
+
attr_reader :device
|
15
|
+
|
16
|
+
# An array of BundleInfo objects corresponding to the applications installed on the
|
17
|
+
# simulator in question
|
18
|
+
attr_reader :bundles
|
19
|
+
|
20
|
+
# Initializes a DeviceListItem instance with a given os, device and bundles array
|
21
|
+
def initialize(os, device, bundles)
|
22
|
+
@os = os
|
23
|
+
@device = device
|
24
|
+
@bundles = bundles
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns a string in <tt>"iPhone 5s (iOS 9.2)"</tt> format
|
28
|
+
def fullName
|
29
|
+
"#{device.name} (#{os.id})"
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns device name
|
33
|
+
def shortName
|
34
|
+
device.name
|
35
|
+
end
|
36
|
+
|
37
|
+
# Same as #fullName
|
38
|
+
def inspect
|
39
|
+
fullName
|
40
|
+
end
|
41
|
+
|
42
|
+
# Same as #inspect
|
43
|
+
def to_s
|
44
|
+
inspect
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
# A class encapsulating logic of searching available OS verions / device models / installed apps
|
50
|
+
# in #xcsim function (i.e. implementation of #xcsim list mode)
|
51
|
+
class GetDeviceList
|
52
|
+
|
53
|
+
# Initializes a GetDeviceList instance with a given device set of OSDevices class
|
54
|
+
def initialize(deviceSet)
|
55
|
+
@deviceSet = deviceSet
|
56
|
+
end
|
57
|
+
|
58
|
+
# Performs search for the iOS simulators matching the provided pattern.
|
59
|
+
# See #xcsim description (List Mode section) for more info.
|
60
|
+
#
|
61
|
+
# Returns an array of DeviceListItem
|
62
|
+
def withPattern(pattern)
|
63
|
+
if pattern.length == 0
|
64
|
+
return allDevices
|
65
|
+
else
|
66
|
+
pattern = parsePattern(pattern)
|
67
|
+
matchingOSes = getMatchingOSList(pattern[:os])
|
68
|
+
|
69
|
+
devicePairs = matchingOSes
|
70
|
+
.map{ |os| os.devices.values.map{ |d| {:os => os, :device => d} } }
|
71
|
+
.flatten
|
72
|
+
|
73
|
+
# try finding a strict match first (disambiguate "iPad Air" from "iPad Air 2")
|
74
|
+
# find partial matches otherwise
|
75
|
+
matchingDevices =
|
76
|
+
strictDeviceMatches(devicePairs, pattern[:device]) ||
|
77
|
+
partialDeviceMatches(devicePairs, pattern[:device]) ||
|
78
|
+
[]
|
79
|
+
|
80
|
+
return matchingDevices.map do |pair|
|
81
|
+
DeviceListItem.new(pair[:os], pair[:device],
|
82
|
+
XCSim::parseInstalledBundles(pair[:device]).values)
|
83
|
+
end
|
84
|
+
.flatten
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns an array of DeviceListItem corresponding to all installed iOS Simulators
|
89
|
+
def allDevices
|
90
|
+
@deviceSet.values.map do |os|
|
91
|
+
os.devices.values.map do |device|
|
92
|
+
DeviceListItem.new(os, device, XCSim::parseInstalledBundles(device).values)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
.flatten
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
def parsePattern(pattern)
|
100
|
+
components = pattern.split(",").map{ |s| s.strip }
|
101
|
+
|
102
|
+
case components.count
|
103
|
+
when 2
|
104
|
+
# assume first pattern is always for OS,
|
105
|
+
# second - for device
|
106
|
+
osPattern = components.first
|
107
|
+
devicePattern = components.last
|
108
|
+
|
109
|
+
when 1
|
110
|
+
# we don't know whether the pattern is for OS or device,
|
111
|
+
# try OS first
|
112
|
+
osPattern = components.first
|
113
|
+
devicePattern = nil
|
114
|
+
matchingOSes = getMatchingOSList(osPattern)
|
115
|
+
|
116
|
+
# if no OSes match, assume the pattern is for device
|
117
|
+
if matchingOSes.empty?
|
118
|
+
osPattern = nil
|
119
|
+
matchingOSes = nil
|
120
|
+
devicePattern = components.first
|
121
|
+
end
|
122
|
+
|
123
|
+
else
|
124
|
+
raise ArgumentError
|
125
|
+
end
|
126
|
+
|
127
|
+
{ :os => osPattern, :device => devicePattern }
|
128
|
+
end
|
129
|
+
|
130
|
+
def strictDeviceMatches(osDevicePairs, pattern)
|
131
|
+
matches = osDevicePairs.select{ |p| p[:device].name == pattern }
|
132
|
+
matches.empty? ? nil : matches
|
133
|
+
end
|
134
|
+
|
135
|
+
def partialDeviceMatches(osDevicePairs, pattern)
|
136
|
+
matches = osDevicePairs.select{ |p| p[:device].name.include? (pattern || "") }
|
137
|
+
matches.empty? ? nil : matches
|
138
|
+
end
|
139
|
+
|
140
|
+
def getMatchingOSList(pattern)
|
141
|
+
return @deviceSet.values.select{ |os| os.id.to_s.include? (pattern || "") }
|
142
|
+
end
|
143
|
+
|
144
|
+
end # class GetDeviceList
|
145
|
+
|
146
|
+
end # module XCSim
|
147
|
+
|
148
|
+
# eof
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module XCSim
|
4
|
+
|
5
|
+
# A collection of DeviceID objects related to a given iOS Simulator OS version
|
6
|
+
class OSDevices
|
7
|
+
|
8
|
+
# An OSID of the iOS Simulator OS version related to the OSDevices collection
|
9
|
+
attr_reader :id
|
10
|
+
|
11
|
+
# A hash of <tt>deviceName => DeviceID</tt>
|
12
|
+
attr_reader :devices
|
13
|
+
|
14
|
+
# Initializes an OSDevices instance with a given OSID and a collection of devices
|
15
|
+
# +id+:: OSID to associate with the OSDevices object
|
16
|
+
# +devices+:: A collection of DeviceID objects. Should support #each method for enumeration.
|
17
|
+
def initialize(id, devices)
|
18
|
+
@id = id
|
19
|
+
|
20
|
+
devicesHash = {}
|
21
|
+
devices.each{ |d| devicesHash[d.name] = d }
|
22
|
+
|
23
|
+
@devices = devicesHash
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns a string in <tt>'iOS 9.0 (5 devices)'</tt> format
|
27
|
+
def inspect
|
28
|
+
"#{@id.inspect} (#{@devices.count} devices)"
|
29
|
+
end
|
30
|
+
|
31
|
+
# Same as #inspect
|
32
|
+
def to_s
|
33
|
+
inspect
|
34
|
+
end
|
35
|
+
|
36
|
+
include Comparable
|
37
|
+
|
38
|
+
# Compares by +id+
|
39
|
+
def <=>(other)
|
40
|
+
@id <=> other.id
|
41
|
+
end
|
42
|
+
|
43
|
+
end # class OSDevicess
|
44
|
+
|
45
|
+
end # module XCSim
|
46
|
+
|
47
|
+
# eof
|
data/lib/xcsim/rbOSID.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module XCSim
|
4
|
+
|
5
|
+
# OSID is used to uniquely identify an iOS Simulator operating system
|
6
|
+
# (including watchOS and tvOS operating systems) and is basically
|
7
|
+
# a pair of OS type string ('iOS', 'watchOS', 'tvOS') and version string
|
8
|
+
# ('9.0', '9.1' etc.)
|
9
|
+
class OSID
|
10
|
+
# Simulated OS type string ('iOS', 'watchOS', 'tvOS')
|
11
|
+
attr_reader :type
|
12
|
+
|
13
|
+
# Simulated OS version string ('7.0', '8.0', '9.0' etc.)
|
14
|
+
attr_reader :version
|
15
|
+
|
16
|
+
# Creates an OSID instance from a string prefixed by a standard
|
17
|
+
# prefix encountered in +device_set.plist+ (+com.apple.CoreSimulator.SimRuntime.+)
|
18
|
+
#
|
19
|
+
# Does not perform any additional validation other than checking the prefix.
|
20
|
+
def self.fromPrefixedString(string)
|
21
|
+
unless string.start_with? @@PREFIX
|
22
|
+
return nil
|
23
|
+
end
|
24
|
+
|
25
|
+
components = string.sub(@@PREFIX, "").split("-")
|
26
|
+
type = components.first
|
27
|
+
version = components[1..-1].join(".")
|
28
|
+
|
29
|
+
return OSID.new(type, version)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Creates an OSID instance from a non-prefixed string. Assumes 'iOS 9.2' format
|
33
|
+
# where components are separated by whitespace and the first component represents
|
34
|
+
# OS type while all others are concatenated and assumed to represent OS version.
|
35
|
+
def self.fromString(string)
|
36
|
+
components = string.split(" ")
|
37
|
+
type = components.first
|
38
|
+
version = components[1..-1].join(".")
|
39
|
+
|
40
|
+
return OSID.new(type, version)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Creates an OSID instance with a given type and version.
|
44
|
+
def initialize(type, version)
|
45
|
+
@type = type
|
46
|
+
@version = version
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns a string in <tt>'iOS 9.2'</tt> format
|
50
|
+
def inspect
|
51
|
+
"#{@type} #{@version}"
|
52
|
+
end
|
53
|
+
|
54
|
+
# Same as #inspect
|
55
|
+
def to_s
|
56
|
+
inspect
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns a string used for indexing OS definitions in +device_set.plist+
|
60
|
+
# in +com.apple.CoreSimulator.SimRuntime.iOS-7-1+ format.
|
61
|
+
def key
|
62
|
+
"#{@@PREFIX}#{@type}-#{@version.gsub(".","-")}"
|
63
|
+
end
|
64
|
+
|
65
|
+
include Comparable
|
66
|
+
|
67
|
+
# Uses @version for comparison (allows using <tt>Array#max</tt> on arrays of OSID
|
68
|
+
# for selecting max version)
|
69
|
+
def <=>(other)
|
70
|
+
@version <=> other.version
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns +inspect.hash+
|
74
|
+
def hash
|
75
|
+
inspect.hash
|
76
|
+
end
|
77
|
+
|
78
|
+
# Checks attribute-wise equality (compares @type and @version)
|
79
|
+
def eql?(other)
|
80
|
+
@type.eql?(other.type) && @version.eql?(other.version)
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
@@PREFIX = "com.apple.CoreSimulator.SimRuntime."
|
85
|
+
end # class OSID
|
86
|
+
|
87
|
+
end # module XCSim
|
88
|
+
|
89
|
+
# eof
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module XCSim
|
4
|
+
|
5
|
+
# Prepares a more human-readable report from an array of DeviceListItem objects.
|
6
|
+
#
|
7
|
+
# Depending on the unique OS versions and device models referenced by the +list+ items,
|
8
|
+
# may return one of the following results:
|
9
|
+
#
|
10
|
+
# List of application bundles:: When +list+ contains a single item
|
11
|
+
# List of device names:: When +list+ contains multiple devices from a single OS version
|
12
|
+
# List of OS version strings:: When +list+ contains a multiple devices from multiple OS version
|
13
|
+
def self.reportFromDeviceList(list)
|
14
|
+
uniqueOSes = list.map{ |item| item.os.id }.uniq
|
15
|
+
uniqueDevices = list.map { |item| item.device.name }.uniq
|
16
|
+
|
17
|
+
countByOS = {}
|
18
|
+
list.each do |item|
|
19
|
+
count = countByOS[item.os.id] || 0
|
20
|
+
countByOS[item.os.id] = count+1
|
21
|
+
end
|
22
|
+
|
23
|
+
if uniqueOSes.empty? || uniqueDevices.empty?
|
24
|
+
raise ArgumentError
|
25
|
+
|
26
|
+
elsif uniqueOSes.count == 1 && uniqueDevices.count == 1
|
27
|
+
list.first.bundles
|
28
|
+
|
29
|
+
elsif uniqueOSes.count == 1
|
30
|
+
list.map{ |item| item.device.name }
|
31
|
+
|
32
|
+
elsif false == (countByOS.values.include? 1)
|
33
|
+
countByOS.map { |id, count| "#{id} (#{count} devices)"}
|
34
|
+
|
35
|
+
else
|
36
|
+
list
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end # module XCSim
|
41
|
+
|
42
|
+
#eof
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require_relative 'rbConstants'
|
4
|
+
require_relative 'rbDeviceSet'
|
5
|
+
require_relative 'rbList'
|
6
|
+
|
7
|
+
# call-seq:
|
8
|
+
# xcsim(:list, pattern) => Array of DeviceListItem
|
9
|
+
# xcsim(:bundle, options) => BundleInfo
|
10
|
+
#
|
11
|
+
# Main function to call when using xcsim gem. #xcsim works in two modes: list mode and bundle
|
12
|
+
# mode. List mode is for getting information about installed iOS Simulators, available OS versions
|
13
|
+
# and bundle IDs of the applications installed on these smiulators. Bundle mode focuses on
|
14
|
+
# individual applications and provides means of getting absolute paths for the application's data
|
15
|
+
# or bundle directories.
|
16
|
+
#
|
17
|
+
# == List mode
|
18
|
+
#
|
19
|
+
# List mode is invoked by calling
|
20
|
+
#
|
21
|
+
# xcsim(:list, pattern)
|
22
|
+
#
|
23
|
+
# Pattern is expected to be a String containing partial match for iOS version / device model pair
|
24
|
+
# in <tt>'iOS 9.2, iPhone 5s'</tt> format. Both of the patterns are optional and coult be omitted,
|
25
|
+
# i.e. the following calls are all valid:
|
26
|
+
#
|
27
|
+
# xcsim(:list, "iOS 9.2, iPhone 5s") # exact match for both iOS version and device
|
28
|
+
# xcsim(:list, "iOS 9.2") # match only iOS version
|
29
|
+
# xcsim(:list, "iPhone 5s") # match only device
|
30
|
+
# xcsim(:list, "iOS") # partial match for iOS version (all iOS simulators)
|
31
|
+
# xcsim(:list, "9.2") # partial match for iOS version (including watchOS, tvOS etc.)
|
32
|
+
# xcsim(:list, "iPad") # partial match for device model (all iPads)
|
33
|
+
# xcsim(:list, "9, iPhone") # partial match for both iOS version and device model
|
34
|
+
#
|
35
|
+
# In list mode #xcsim function returns an array of DeviceListItem object representing
|
36
|
+
# iOS Simulators found, which match the pattern provided. Partial matches are processed
|
37
|
+
# in a case-sensitive manner (basically, it finds all devices, whose OS version has the provided
|
38
|
+
# OS version pattern as a substring and whose device model contains the provided device model
|
39
|
+
# pattern as a substring).
|
40
|
+
#
|
41
|
+
# Each DeviceListItem will contain info about applications installed on the corresponding
|
42
|
+
# Simulator, excluding system apps (Safari et al.)
|
43
|
+
#
|
44
|
+
# Note that since #xcsim signature is
|
45
|
+
#
|
46
|
+
# def xcsim(*args)
|
47
|
+
#
|
48
|
+
# multiple arguments may be provided to the function. In list mode all arguments are concatenated
|
49
|
+
# into a single +pattern+ string by using single whitespace as a separator.
|
50
|
+
#
|
51
|
+
# == Bundle mode
|
52
|
+
#
|
53
|
+
# Bundle mode is invoked by calling
|
54
|
+
#
|
55
|
+
# xcsim(:bundle, options)
|
56
|
+
#
|
57
|
+
# Options is expected to be a Hash with the following keys:
|
58
|
+
#
|
59
|
+
# +:bundleID+:: Specifies the bundle ID of the application in question. #xcsim will then try
|
60
|
+
# to locate the bundle with the ID provided and return the corresponding BundleInfo.
|
61
|
+
# This is the only required option.
|
62
|
+
#
|
63
|
+
# Bundle ID can be partially matched. Actual bundle IDs of the applications found
|
64
|
+
# in the iOS Simulator are checked for having the provided bundle ID suffix, so
|
65
|
+
# passing <tt>"application"</tt> as the +:bundleID+ option will match bundle ID
|
66
|
+
# <tt>"com.yourcompany.application"</tt> for example. If multiple matches are found,
|
67
|
+
# the result is ambiguous and #xcsim will raise an error. See Errors section for more
|
68
|
+
# info.
|
69
|
+
#
|
70
|
+
# +:os+:: Specifies, which iOS version to use when searching in <tt>"iOS 9.2"</tt> format.
|
71
|
+
# If omitted, #xcsim will default the OS version to the latest one found in the
|
72
|
+
# parsed +device_set.plist+
|
73
|
+
#
|
74
|
+
# +:device+:: Specifies, which device model to use when searching in <tt>"iPad Air 2"</tt>
|
75
|
+
# format. If omitted, #xcsim will default the device model to <tt>"iPhone 5s"</tt>
|
76
|
+
#
|
77
|
+
# Note that the +:os+ and +:device+ options do not perform partial matching to the contrary of
|
78
|
+
# what the list mode does.
|
79
|
+
#
|
80
|
+
# #xcsim will return a BundleInfo instance for bundle matching the options or raise an error.
|
81
|
+
#
|
82
|
+
# == Errors
|
83
|
+
#
|
84
|
+
# #xcsim may raise errors of several types when matching its results:
|
85
|
+
#
|
86
|
+
# ArgumentError:: An ArgumentError is raised if the parameters provided do not make sense
|
87
|
+
# as described above. That is, if in list mode multiple comma-separated
|
88
|
+
# components are found in the pattern string, or in bundle mode +:bundleID+
|
89
|
+
# parameter is not found, an ArgumentError will be raised.
|
90
|
+
#
|
91
|
+
# NonUniqueBundleIDError:: XCSim::NonUniqueBundleIDError is raised when multiple data or bundle
|
92
|
+
# directories are found matching the same bundle ID (should not generally
|
93
|
+
# happen since bundle IDs are unique in iOS Simulator) or when partial
|
94
|
+
# match of bundle ID in bundle mode finds multiple results.
|
95
|
+
#
|
96
|
+
# OSNotFoundError:: XCSim::OSNotFoundError is raised in bundle mode when OS version specified
|
97
|
+
# by the +:os+ option cannot be found in installed iOS simulators list.
|
98
|
+
#
|
99
|
+
# DeviceNotFoundError:: XCSim::DeviceNotFoundError is raised in bundle mode when device model
|
100
|
+
# specified by the +:device+ option cannot be found in installed iOS
|
101
|
+
# simulators list for the selected OS version.
|
102
|
+
#
|
103
|
+
# BundleNotFoundError:: XCSim::BundleNotFoundError is raised in bundle mode when the bundle ID
|
104
|
+
# specified by the +:bundleID+ option does not match any application
|
105
|
+
# installed on the selected iOS Simulator.
|
106
|
+
#
|
107
|
+
def xcsim(*args)
|
108
|
+
case args.first
|
109
|
+
when :list
|
110
|
+
return XCSim::GetDeviceList.new(XCSim.deviceSet).withPattern(args[1..-1].join(" "))
|
111
|
+
|
112
|
+
when :bundle
|
113
|
+
raise ArgumentError if args.count != 2
|
114
|
+
return XCSim::GetBundle.new(XCSim.deviceSet).withOptions(args.last)
|
115
|
+
|
116
|
+
else
|
117
|
+
raise ArgumentError
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# eof
|
data/lib/xcsim.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'cfpropertylist'
|
5
|
+
rescue LoadError
|
6
|
+
puts "require 'cfpropertylist' failed! Consider running 'gem install CFPropertyList'"
|
7
|
+
exit
|
8
|
+
end
|
9
|
+
|
10
|
+
require_relative 'xcsim/rbXCSim'
|
11
|
+
require_relative 'xcsim/rbReports'
|
12
|
+
|
13
|
+
# eof
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xcsim
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Egor Chiglintsev
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-03-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: CFPropertyList
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.2'
|
27
|
+
description: |
|
28
|
+
xcsim is a command-line utility to simplify opening iOS Simulator application/data directories. It's as simple as running `xcsim com.yourcompany.application`.
|
29
|
+
email: egor.chiglintsev@gmail.com
|
30
|
+
executables:
|
31
|
+
- xcsim
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- LICENSE
|
36
|
+
- README.md
|
37
|
+
- bin/xcsim
|
38
|
+
- lib/xcsim.rb
|
39
|
+
- lib/xcsim/rbAppBundles.rb
|
40
|
+
- lib/xcsim/rbBundleInfo.rb
|
41
|
+
- lib/xcsim/rbConstants.rb
|
42
|
+
- lib/xcsim/rbDeviceID.rb
|
43
|
+
- lib/xcsim/rbDeviceSet.rb
|
44
|
+
- lib/xcsim/rbList.rb
|
45
|
+
- lib/xcsim/rbOSDevices.rb
|
46
|
+
- lib/xcsim/rbOSID.rb
|
47
|
+
- lib/xcsim/rbReports.rb
|
48
|
+
- lib/xcsim/rbXCSim.rb
|
49
|
+
homepage: https://github.com/wanderwaltz/xcsim
|
50
|
+
licenses:
|
51
|
+
- MIT
|
52
|
+
metadata: {}
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
requirements: []
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 2.2.2
|
70
|
+
signing_key:
|
71
|
+
specification_version: 4
|
72
|
+
summary: Open iOS Simulator application/data directories by bundle ID
|
73
|
+
test_files: []
|