@@ -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