@@ -24,11 +24,122 @@ devices = {
2424 } ,
2525 "visionos" => {
2626 1 => [ "Apple Vision Pro (at 2732x2048) (1.2)" ] ,
27- 2 => [ "Apple Vision Pro (2.5)" ] ,
27+ 2 => [ "Apple Vision Pro (at 2732x2048) ( 2.5)" ] ,
2828 26 => [ "Apple Vision Pro (26.0)" ] ,
2929 } ,
3030}
3131
32+ before_all do |lane , options |
33+ next if lane == :create_simulators
34+ create_simulators ( platform : options [ :platform ] , version : options [ :version ] )
35+ end
36+
37+ lane :create_simulators do |options |
38+ require 'json'
39+ require 'set'
40+
41+ # map Fastfile platform keys to display names used by CoreSimulator runtimes
42+ platforms_to_os = {
43+ "ios" => "iOS" ,
44+ "tvos" => "tvOS" ,
45+ "watchos" => "watchOS" ,
46+ "visionos" => "visionOS" ,
47+ }
48+
49+ # Build lookup tables from CoreSimulator for robust name→identifier mapping
50+ begin
51+ list_json = sh ( "xcrun simctl list -j" , log : false )
52+ list = JSON . parse ( list_json )
53+ devtypes = list [ "devicetypes" ] || [ ]
54+ runtimes = list [ "runtimes" ] || [ ]
55+
56+ # Build a set of existing simulator names to prevent duplicates
57+ devices_json = sh ( "xcrun simctl list -j devices" , log : false )
58+ devices_list = JSON . parse ( devices_json )
59+ existing_names = ( devices_list [ "devices" ] || { } ) . values . flatten . map { |d | d [ "name" ] } . compact . to_set
60+ rescue => e
61+ UI . message ( "Failed to read simctl lists: #{ e } " )
62+ devtypes = [ ]
63+ runtimes = [ ]
64+ existing_names = Set . new
65+ end
66+
67+ device_name_to_id = devtypes . each_with_object ( { } ) do |dt , h |
68+ name = dt [ "name" ] ; id = dt [ "identifier" ]
69+ h [ name ] = id if name && id
70+ end
71+
72+ runtime_name_to_id = runtimes . each_with_object ( { } ) do |rt , h |
73+ next unless rt [ "isAvailable" ]
74+ name = rt [ "name" ] ; id = rt [ "identifier" ]
75+ h [ name ] = id if name && id
76+ end
77+
78+ # Fallback builders when exact matches are not present in the lookup tables
79+ build_device_type_id = proc do |device_name |
80+ s = device_name . gsub ( /[()]/ , '' ) . gsub ( /\s +/ , '-' ) . gsub ( /[^A-Za-z0-9-]/ , '' )
81+ "com.apple.CoreSimulator.SimDeviceType.#{ s } "
82+ end
83+
84+ build_runtime_id = proc do |os_name , version |
85+ "com.apple.CoreSimulator.SimRuntime.#{ os_name } -#{ version . tr ( '.' , '-' ) } "
86+ end
87+
88+ platform_opt = options && options [ :platform ] ? options [ :platform ] . to_s . downcase : nil
89+ version_opt = options && options [ :version ] ? options [ :version ] . to_i : nil
90+
91+ local_devices = if platform_opt && devices . key? ( platform_opt )
92+ subset_versions = devices [ platform_opt ]
93+ if version_opt && subset_versions . key? ( version_opt )
94+ { platform_opt => { version_opt => subset_versions [ version_opt ] } }
95+ else
96+ { platform_opt => subset_versions }
97+ end
98+ else
99+ devices
100+ end
101+
102+ local_devices . each do |platform , versions |
103+ os_name = platforms_to_os [ platform ]
104+ next if os_name . nil?
105+
106+ versions . values . flatten . each do |descriptor |
107+ # descriptor examples:
108+ # "iPhone 14 Pro (16.4)"
109+ # "iPad Pro (11-inch) (4th generation) (16.4)"
110+ # "Apple Vision Pro (2.5)"
111+ begin
112+ # Parse trailing "(x.y)" and derive device name
113+ if descriptor =~ /\s *\( ([^()]+)\) \s *\z /
114+ runtime_version = $1
115+ device_name = descriptor . sub ( /\s *\( [^()]+\) \s *\z / , '' )
116+ else
117+ UI . message ( "Could not parse runtime version from '#{ descriptor } ', skipping" )
118+ next
119+ end
120+
121+ runtime_name = "#{ os_name } #{ runtime_version } "
122+
123+ device_type_id = device_name_to_id [ device_name ] || build_device_type_id . call ( device_name )
124+ runtime_id = runtime_name_to_id [ runtime_name ] || build_runtime_id . call ( os_name , runtime_version )
125+
126+ # Use the device name without the version suffix as the simulator name
127+ sim_name = device_name
128+
129+ if existing_names . include? ( sim_name )
130+ UI . message ( "Already exists: #{ sim_name } (#{ runtime_version } ), skipping" )
131+ next
132+ end
133+
134+ sh ( %(xcrun simctl create "#{ sim_name } " "#{ device_type_id } " "#{ runtime_id } " || true) )
135+ existing_names . add ( sim_name )
136+ rescue => e
137+ UI . message ( "Skipping #{ descriptor } : #{ e } " )
138+ end
139+ end
140+ end
141+ end
142+
32143lane :build do |options |
33144 platform = options [ :platform ] . to_s . downcase
34145 version = options [ :version ] . to_i
0 commit comments