@@ -12,6 +12,7 @@ import (
12
12
13
13
"github.com/danielpaulus/go-ios/ios"
14
14
"github.com/danielpaulus/go-ios/ios/coredevice"
15
+ "github.com/danielpaulus/go-ios/ios/openstdio"
15
16
"github.com/danielpaulus/go-ios/ios/xpc"
16
17
"github.com/google/uuid"
17
18
"howett.net/plist"
@@ -22,6 +23,7 @@ import (
22
23
type Connection struct {
23
24
conn * xpc.Connection
24
25
deviceId string
26
+ device ios.DeviceEntry
25
27
}
26
28
27
29
const (
@@ -39,13 +41,28 @@ func New(deviceEntry ios.DeviceEntry) (*Connection, error) {
39
41
return nil , fmt .Errorf ("new: %w" , err )
40
42
}
41
43
42
- return & Connection {conn : xpcConn , deviceId : uuid .New ().String ()}, nil
44
+ return & Connection {conn : xpcConn , deviceId : uuid .New ().String (), device : deviceEntry }, nil
43
45
}
44
46
45
- // AppLaunch represents the result of launching an app on the device for iOS17+.
46
- // It contains the PID of the launched app.
47
- type AppLaunch struct {
48
- Pid int
47
+ // LaunchedAppWithStdIo is the launched app with a connection to the stdio-socket
48
+ type LaunchedAppWithStdIo struct {
49
+ stdIoConnection openstdio.Connection
50
+ Pid int
51
+ }
52
+
53
+ // Read reads from the stdio socket of the launched app
54
+ func (a LaunchedAppWithStdIo ) Read (p []byte ) (n int , err error ) {
55
+ return a .stdIoConnection .Read (p )
56
+ }
57
+
58
+ // Write reads from the stdio socket of the launched app
59
+ func (a LaunchedAppWithStdIo ) Write (p []byte ) (n int , err error ) {
60
+ return a .stdIoConnection .Write (p )
61
+ }
62
+
63
+ // Close closes the connection to stdio-socket of the launched app
64
+ func (a LaunchedAppWithStdIo ) Close () error {
65
+ return a .stdIoConnection .Close ()
49
66
}
50
67
51
68
// Process represents a process running on the device for iOS17+.
@@ -56,21 +73,55 @@ type Process struct {
56
73
}
57
74
58
75
// LaunchApp launches an app on the device with the given bundleId and arguments for iOS17+.
59
- func (c * Connection ) LaunchApp (bundleId string , args []interface {}, env map [string ]interface {}, options map [string ]interface {}, terminateExisting bool ) (AppLaunch , error ) {
60
- msg := buildAppLaunchPayload (c .deviceId , bundleId , args , env , options , terminateExisting )
76
+ // On a successful launch it returns the PID of the launched process.
77
+ func (c * Connection ) LaunchApp (bundleId string , args []interface {}, env map [string ]interface {}, options map [string ]interface {}, terminateExisting bool ) (int , error ) {
78
+ pid , err := c .launchApp (bundleId , args , env , options , terminateExisting , map [string ]any {})
79
+ if err != nil {
80
+ return 0 , fmt .Errorf ("LaunchApp: failed to launch app: %w" , err )
81
+ }
82
+ return pid , nil
83
+ }
84
+
85
+ // LaunchAppWithStdIo launches an app and connects to the stdio-socket
86
+ // the returned value implements the io.ReadWriteCloser interface and needs to be closed when finished using the stdio-socket
87
+ func (c * Connection ) LaunchAppWithStdIo (bundleId string , args []interface {}, env map [string ]interface {}, options map [string ]interface {}, terminateExisting bool ) (LaunchedAppWithStdIo , error ) {
88
+ stdio , err := openstdio .NewOpenStdIoSocket (c .device )
89
+ if err != nil {
90
+ return LaunchedAppWithStdIo {}, fmt .Errorf ("LaunchAppWithStdIo: failed to open stdio socket: %w" , err )
91
+ }
92
+
93
+ // this is also how Xcode handles it. It uses the same socket for stdOut/stdErr/stdIn
94
+ stdIoConfig := map [string ]any {
95
+ "standardInput" : stdio .ID ,
96
+ "standardOutput" : stdio .ID ,
97
+ "standardError" : stdio .ID ,
98
+ }
99
+
100
+ pid , err := c .launchApp (bundleId , args , env , options , terminateExisting , stdIoConfig )
101
+ if err != nil {
102
+ return LaunchedAppWithStdIo {}, fmt .Errorf ("LaunchAppWithStdIo: failed to launch app: %w" , err )
103
+ }
104
+ return LaunchedAppWithStdIo {
105
+ stdIoConnection : stdio ,
106
+ Pid : pid ,
107
+ }, nil
108
+ }
109
+
110
+ func (c * Connection ) launchApp (bundleId string , args []interface {}, env map [string ]interface {}, options map [string ]interface {}, terminateExisting bool , stdio map [string ]any ) (int , error ) {
111
+ msg := buildAppLaunchPayload (c .deviceId , bundleId , args , env , options , terminateExisting , map [string ]any {})
61
112
err := c .conn .Send (msg , xpc .HeartbeatRequestFlag )
62
113
if err != nil {
63
- return AppLaunch {} , fmt .Errorf ("LaunchApp : failed to send launch-app request: %w" , err )
114
+ return 0 , fmt .Errorf ("launchApp : failed to send launch-app request: %w" , err )
64
115
}
65
116
m , err := c .conn .ReceiveOnServerClientStream ()
66
117
if err != nil {
67
- return AppLaunch {} , fmt .Errorf ("launchApp2 : %w" , err )
118
+ return 0 , fmt .Errorf ("launchApp: failed to read response : %w" , err )
68
119
}
69
120
pid , err := pidFromResponse (m )
70
121
if err != nil {
71
- return AppLaunch {} , fmt .Errorf ("launchApp3 : %w" , err )
122
+ return 0 , fmt .Errorf ("launchApp: failed to get PID : %w" , err )
72
123
}
73
- return AppLaunch { Pid : int (pid )} , nil
124
+ return int (pid ), nil
74
125
}
75
126
76
127
// Close closes the connection to the appservice
@@ -181,7 +232,7 @@ func (p Process) ExecutableName() string {
181
232
return file
182
233
}
183
234
184
- func buildAppLaunchPayload (deviceId string , bundleId string , args []interface {}, env map [string ]interface {}, options map [string ]interface {}, terminateExisting bool ) map [string ]interface {} {
235
+ func buildAppLaunchPayload (deviceId string , bundleId string , args []interface {}, env map [string ]interface {}, options map [string ]interface {}, terminateExisting bool , stdIo map [ string ] any ) map [string ]interface {} {
185
236
platformSpecificOptions := bytes .NewBuffer (nil )
186
237
plistEncoder := plist .NewBinaryEncoder (platformSpecificOptions )
187
238
err := plistEncoder .Encode (options )
@@ -207,7 +258,7 @@ func buildAppLaunchPayload(deviceId string, bundleId string, args []interface{},
207
258
},
208
259
"workingDirectory" : nil ,
209
260
},
210
- "standardIOIdentifiers" : map [ string ] interface {}{} ,
261
+ "standardIOIdentifiers" : stdIo ,
211
262
})
212
263
}
213
264
0 commit comments