@@ -51,27 +51,78 @@ import OpenAPIURLSession
5151@main
5252struct HttpUsageDemo {
5353 static func main () async throws {
54-
55- let yourAccountToken = ProcessInfo.processInfo .environment [" TOKEN" ] ?? " "
54+ let token = ProcessInfo.processInfo .environment [" TOKEN" ] ?? " "
55+ let serverId = ProcessInfo.processInfo .environment [" SERVER" ] ?? " "
56+ guard ! token.isEmpty else {
57+ print (" Missing env TOKEN. Example: TOKEN=... swift run HTTPUsageDemo" )
58+ return
59+ }
5660
5761 let client = Client (
58- serverURL : try ! Servers.server1 (),
62+ serverURL : try ! Servers.Server1 . url (),
5963 transport : URLSessionTransport (),
60- middlewares : [AuthenticationMiddleware (token : yourAccountToken )]
64+ middlewares : [AuthenticationMiddleware (token : token )]
6165 )
62- let response = try await client.getAccount ()
66+ let accountResponse = try await client.getAccount ()
67+
68+ switch accountResponse {
69+ case .ok (let ok):
70+ let account = try ok.body .json .data
71+ print (" Account: \( account? .name ?? " -" ) " )
72+ case .forbidden (let forbidden):
73+ let json = try forbidden.body .json
74+ print (" Forbidden: \( json.error ?? " -" ) " )
75+ case .undocumented (let statusCode, let unknownPayload):
76+ print (" Unexpected status: \( statusCode ) , payload: \( unknownPayload ) " )
77+ }
6378
64- switch response {
79+ let serversResponse = try await client.getServers ()
80+ switch serversResponse {
6581 case .ok (let ok):
66- if let data = try ok.body.json.data,
67- let name = data.name {
68- print (" account: \( name ) " )
82+ let servers = try ok.body .json .data ?? []
83+ print (" Servers: \( servers.count ) " )
84+ if let first = servers.first {
85+ print (" First server: \( first.id ?? " -" ) \( first.name ?? " -" ) status=\( first.status ? .rawValue ?? -1 ) " )
6986 }
87+ case .badRequest (let badRequest):
88+ let json = try badRequest.body .json
89+ print (" Bad request: \( json.error ?? " -" ) " )
7090 case .forbidden (let forbidden):
7191 let json = try forbidden.body .json
72- print (json.error ?? " " )
92+ print (" Forbidden: \( json.error ?? " -" ) " )
93+ case .notFound (let notFound):
94+ let json = try notFound.body .json
95+ print (" Not found: \( json.error ?? " -" ) " )
96+ case .internalServerError (let internalServerError):
97+ let json = try internalServerError.body .json
98+ print (" Internal error: \( json.error ?? " -" ) " )
7399 case .undocumented (let statusCode, let unknownPayload):
74- print (" statusCode:\( statusCode ) , payload: \( unknownPayload ) " )
100+ print (" Unexpected status: \( statusCode ) , payload: \( unknownPayload ) " )
101+ }
102+
103+ if ! serverId.isEmpty {
104+ let serverResponse = try await client.getServer (path : .init (serverId : serverId))
105+ switch serverResponse {
106+ case .ok (let ok):
107+ let server = try ok.body .json .data
108+ print (" Server: \( server? .id ?? " -" ) \( server? .name ?? " -" ) " )
109+ case .badRequest (let badRequest):
110+ let json = try badRequest.body .json
111+ print (" Bad request: \( json.error ?? " -" ) " )
112+ case .notFound (let notFound):
113+ let json = try notFound.body .json
114+ print (" Not found: \( json.error ?? " -" ) " )
115+ case .forbidden (let forbidden):
116+ let json = try forbidden.body .json
117+ print (" Forbidden: \( json.error ?? " -" ) " )
118+ case .internalServerError (let internalServerError):
119+ let json = try internalServerError.body .json
120+ print (" Internal error: \( json.error ?? " -" ) " )
121+ case .undocumented (let statusCode, let unknownPayload):
122+ print (" Unexpected status: \( statusCode ) , payload: \( unknownPayload ) " )
123+ }
124+ } else {
125+ print (" Tip: set env SERVER=... to query a specific server." )
75126 }
76127 }
77128}
@@ -108,6 +159,9 @@ let package = Package(
108159
109160```
110161
162+ Note:
163+ - ` ExarotonWebSocketAPI.delegate ` is ` weak ` . Keep a strong reference to your handler (e.g. store it as a property), otherwise callbacks may stop unexpectedly.
164+
111165Use ExarotonWebSocket:
112166
113167``` swift
@@ -119,38 +173,98 @@ import Starscream
119173struct WebSocketUsageDemo {
120174
121175 static func main () async throws {
122-
123- let socket = ExarotonWebSocketAPI (
124- token : ProcessInfo.processInfo .environment [" TOKEN" ] ?? " your_account_token" ,
125- serverId : ProcessInfo.processInfo .environment [" SERVER" ] ?? " your_server_id" ,
126- delegate : ServerEventHandler ()
127- )
128-
129- socket.client .connect ()
130- try await wait (for : socket.timeout )
131-
132- let consoleStreamMessage = ExarotonMessage (
133- stream : .console ,
134- type : StreamType.start ,
135- data : [" tail" : 2 ]
136- )
137- socket.client .write (stringData : try consoleStreamMessage.toData ) {
138- print (" console stream start completed!" )
176+ let token = ProcessInfo.processInfo .environment [" TOKEN" ] ?? " "
177+ let serverId = ProcessInfo.processInfo .environment [" SERVER" ] ?? " "
178+ guard ! token.isEmpty , ! serverId.isEmpty else {
179+ print (" Missing env TOKEN or SERVER. Example: TOKEN=... SERVER=... swift run WebSocketUsageDemo" )
180+ return
181+ }
182+
183+ let ready = ReadySignal ()
184+ let handler = ServerEventHandler (ready : ready)
185+ let socket = ExarotonWebSocketAPI (token : token, serverId : serverId, delegate : handler)
186+
187+ socket.connect ()
188+
189+ let didBecomeReady = await ready.wait (seconds : socket.timeout )
190+ guard didBecomeReady else {
191+ print (" Timed out waiting for ready" )
192+ socket.disconnect ()
193+ return
194+ }
195+
196+ try socket.startStream (.console , tail : 10 ) {
197+ print (" console stream start sent" )
139198 }
140199
141- try await wait (for : socket.timeout )
142- socket.client .disconnect ()
200+ try socket.sendConsoleCommand (" say Hello from WebSocketUsageDemo" ) {
201+ print (" console command sent" )
202+ }
203+
204+ try await sleep (seconds : 3 )
205+ try socket.stopStream (.console ) {
206+ print (" console stream stop sent" )
207+ }
208+
209+ try await sleep (seconds : 1 )
210+ socket.disconnect ()
211+ }
212+
213+ static func sleep (seconds : Double ) async throws {
214+ let ns = UInt64 (max (0 , seconds) * 1_000_000_000 )
215+ try await Task.sleep (nanoseconds : ns)
216+ }
217+ }
218+
219+ actor ReadySignal {
220+ private var continuation: CheckedContinuation<Void , Never >?
221+ private var isSignaled = false
222+
223+ func signal () {
224+ isSignaled = true
225+ continuation? .resume ()
226+ continuation = nil
143227 }
144228
145- static func wait (for minutes : Double ) async throws {
146- try await Task.sleep (nanoseconds : UInt64 (1_000_000_000 * minutes))
229+ func wait (seconds : Double ) async -> Bool {
230+ if isSignaled { return true }
231+ return await withTaskGroup (of : Bool .self ) { group in
232+ group.addTask {
233+ await withCheckedContinuation { continuation in
234+ Task { await self ._install (continuation) }
235+ }
236+ return true
237+ }
238+ group.addTask {
239+ let ns = UInt64 (max (0 , seconds) * 1_000_000_000 )
240+ try ? await Task.sleep (nanoseconds : ns)
241+ return false
242+ }
243+ let result = await group.next () ?? false
244+ group.cancelAll ()
245+ return result
246+ }
247+ }
248+
249+ private func _install (_ continuation : CheckedContinuation<Void , Never >) {
250+ if isSignaled {
251+ continuation.resume ()
252+ return
253+ }
254+ self .continuation = continuation
147255 }
148256}
149257
150258final class ServerEventHandler : ExarotonServerEventHandlerProtocol {
259+ let ready: ReadySignal
260+
261+ init (ready : ReadySignal) {
262+ self .ready = ready
263+ }
151264
152265 func onReady (serverID : String ? ) {
153266 print (" server ready: \( serverID ?? " " ) " )
267+ Task { await ready.signal () }
154268 }
155269
156270 func onConnected () {
@@ -207,19 +321,21 @@ final class ServerEventHandler: ExarotonServerEventHandlerProtocol {
207321 }
208322 }
209323
210- // MARK: WebSocketDelegate
324+ func onError (_ error : Error ) {
325+ print (" error: \( error.localizedDescription ) " )
326+ }
327+
211328 func didReceive (event : Starscream.WebSocketEvent, client : any Starscream.WebSocketClient) {
212- // all events, if you need process them your self
213329 }
214330}
215331```
216332For More Use Cases:
217333- ππ» [ Send Message] [ websocket send message cases ]
218334- ππ» [ Receive Message] [ websocket message receive handler ]
219335
220- ## Developemnt π¨π»βπ»
336+ ## Development π¨π»βπ»
221337
222- If you want to contribute to this project, you can use your Mac device and install the Xcode` (>= 15.3 ) ` to get start
338+ If you want to contribute to this project, you can use your Mac device and install the Xcode` (>= 15.4 ) ` to get start
223339
224340Run shell command as follow to get the project and open it with xcode editor:
225341
@@ -261,6 +377,6 @@ If things goes well, you will see the unittests run and success or fail as follo
261377[ Exaroton OpenAPI Doc ] : < https://developers.exaroton.com/openapi.yaml >
262378[ Swagger Editor ] : < https://editor-next.swagger.io/ >
263379[ Swift OpenAPI Generator ] : < https://swiftpackageindex.com/apple/swift-openapi-generator >
264- [ openapi http client cases ] : < https://github.com/wangzhizhou/ExarotonAPI/blob/main/Tests/ExarotonHTTPTests/ExarotonHTTPTests .swift >
265- [ websocket send message cases ] : < https://github.com/wangzhizhou/ExarotonAPI/blob/main/Tests/ExarotonWebSocketTests/ExarotonWebSocketTests .swift >
380+ [ openapi http client cases ] : < https://github.com/wangzhizhou/ExarotonAPI/blob/main/Tests/ExarotonHTTPTests/ExarotonHTTPUnitTests .swift >
381+ [ websocket send message cases ] : < https://github.com/wangzhizhou/ExarotonAPI/blob/main/Sources/ExarotonWebSocket/ExarotonWebSocketAPI .swift >
266382[ websocket message receive handler ] : < https://github.com/wangzhizhou/ExarotonAPI/blob/main/Tests/ExarotonWebSocketTests/ExarotonWebSocketEventDelegateHandler.swift >
0 commit comments