66 "fmt"
77 "os"
88 "os/exec"
9+ "path/filepath"
910 "regexp"
1011 "strconv"
1112 "strings"
@@ -15,6 +16,7 @@ import (
1516 "github.com/mobile-next/mobilecli/devices/wda"
1617 "github.com/mobile-next/mobilecli/devices/wda/mjpeg"
1718 "github.com/mobile-next/mobilecli/utils"
19+ "howett.net/plist"
1820)
1921
2022const (
@@ -31,6 +33,14 @@ type AppInfo struct {
3133 CFBundleVersion string `json:"CFBundleVersion"`
3234}
3335
36+ // devicePlist represents the structure of device.plist
37+ type devicePlist struct {
38+ UDID string `plist:"UDID"`
39+ Name string `plist:"name"`
40+ Runtime string `plist:"runtime"`
41+ State int `plist:"state"`
42+ }
43+
3444// Simulator represents an iOS simulator device
3545type Simulator struct {
3646 Name string `json:"name"`
@@ -110,53 +120,58 @@ func runSimctl(args ...string) ([]byte, error) {
110120 return output , nil
111121}
112122
113- // getSimulators executes 'xcrun simctl list --json' and returns the parsed response
123+ // getSimulators reads simulator information from the filesystem
114124func GetSimulators () ([]Simulator , error ) {
115- output , err := runSimctl ( "list" , "--json" )
125+ homeDir , err := os . UserHomeDir ( )
116126 if err != nil {
117- return nil , fmt .Errorf ("failed to execute xcrun simctl list: %w" , err )
118- }
119-
120- var simulators map [string ]interface {}
121- if err := json .Unmarshal (output , & simulators ); err != nil {
122- return nil , fmt .Errorf ("failed to parse simulator list JSON: %w" , err )
127+ return nil , fmt .Errorf ("failed to get user home directory: %w" , err )
123128 }
124129
125- devices , ok := simulators ["devices" ].(map [string ]interface {})
126- if ! ok {
127- return nil , fmt .Errorf ("unexpected format in simulator list: devices not found or not a map" )
130+ devicesPath := filepath .Join (homeDir , "Library" , "Developer" , "CoreSimulator" , "Devices" )
131+ entries , err := os .ReadDir (devicesPath )
132+ if err != nil {
133+ return nil , fmt .Errorf ("failed to read devices directory: %w" , err )
128134 }
129135
130- var filteredDevices []Simulator
136+ var simulators []Simulator
131137
132- for runtimeName , deviceList := range devices {
133- deviceArray , ok := deviceList .([]interface {})
134- if ! ok {
138+ for _ , entry := range entries {
139+ if ! entry .IsDir () {
135140 continue
136141 }
137142
138- for _ , device := range deviceArray {
139- deviceMap , ok := device .(map [string ]interface {})
140- if ! ok {
141- continue
142- }
143+ plistPath := filepath .Join (devicesPath , entry .Name (), "device.plist" )
144+ data , err := os .ReadFile (plistPath )
145+ if err != nil {
146+ // skip devices without device.plist
147+ continue
148+ }
143149
144- name , _ := deviceMap ["name" ].(string )
145- udid , _ := deviceMap ["udid" ].(string )
146- state , _ := deviceMap ["state" ].(string )
150+ var device devicePlist
151+ if _ , err := plist .Unmarshal (data , & device ); err != nil {
152+ // skip devices with invalid plist
153+ continue
154+ }
147155
148- simulator := Simulator {
149- Name : name ,
150- UDID : udid ,
151- State : state ,
152- Runtime : runtimeName ,
153- }
156+ // convert state integer to string
157+ // state 1 = Shutdown (offline)
158+ // state 3 = Booted (online)
159+ stateStr := "Shutdown"
160+ if device .State == 3 {
161+ stateStr = "Booted"
162+ }
154163
155- filteredDevices = append (filteredDevices , simulator )
164+ simulator := Simulator {
165+ Name : device .Name ,
166+ UDID : device .UDID ,
167+ State : stateStr ,
168+ Runtime : device .Runtime ,
156169 }
170+
171+ simulators = append (simulators , simulator )
157172 }
158173
159- return filteredDevices , nil
174+ return simulators , nil
160175}
161176
162177// filterSimulatorsByDownloadsDirectory filters simulators that have been booted at least once
0 commit comments