Skip to content

Commit c55ba7d

Browse files
authored
Update README.md
1 parent 6b85886 commit c55ba7d

File tree

1 file changed

+18
-290
lines changed

1 file changed

+18
-290
lines changed

README.md

Lines changed: 18 additions & 290 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ To install, simply:
1616

1717
#### Swift Package Manager
1818

19+
#### ⚠️ For iOS before 13 version, please use 0.4.0
20+
1921
Add the following line to your `Package.swift`
2022

2123
```swift
2224
// ...
23-
.package(name: "ActionCableSwift", url: "https://github.com/nerzh/Action-Cable-Swift.git", from: "0.3.2"),
25+
.package(name: "ActionCableSwift", url: "https://github.com/nerzh/Action-Cable-Swift.git", from: "1.0.0"),
2426
targets: [
2527
.target(
2628
name: "YourPackageName",
@@ -30,292 +32,8 @@ Add the following line to your `Package.swift`
3032
// ...
3133
```
3234

33-
#### Cocoa Pods
34-
35-
Add the following line to your `Podfile`
36-
37-
```ruby
38-
pod 'ActionCableSwift'
39-
```
40-
41-
and you can import ActionCableSwift
42-
43-
```swift
44-
import ActionCableSwift
45-
```
4635
# Usage
4736

48-
---
49-
50-
## Your WebSocketService should to implement the `ACWebSocketProtocol` protocol.
51-
52-
---
53-
54-
### Use with [Websocket-kit](https://github.com/vapor/websocket-kit)
55-
56-
#### I highly recommend not using Starscream to implement a WebSocket, because they have a strange implementation that does not allow conveniently reconnecting to a remote server after disconnecting. There is also a cool and fast alternative from the [Swift Server Work Group (SSWG)](https://swift.org/server/), package named [Websocket-kit](https://github.com/vapor/websocket-kit).
57-
58-
[Websocket-kit](https://github.com/vapor/websocket-kit) is SPM(Swift Package Manager) client library built on [Swift-NIO](https://github.com/apple/swift-nio)
59-
60-
Package.swift
61-
```swift
62-
// ...
63-
dependencies: [
64-
.package(name: "ActionCableSwift", url: "https://github.com/nerzh/Action-Cable-Swift.git", from: "0.3.0"),
65-
.package(name: "websocket-kit", url: "https://github.com/vapor/websocket-kit.git", .upToNextMinor(from: "2.0.0"))
66-
],
67-
targets: [
68-
.target(
69-
name: "YourPackageName",
70-
dependencies: [
71-
.product(name: "ActionCableSwift", package: "ActionCableSwift"),
72-
.product(name: "WebSocketKit", package: "websocket-kit")
73-
])
74-
// ...
75-
```
76-
77-
78-
or inside xcode
79-
80-
81-
<img width="733" alt="Снимок экрана 2020-08-28 в 14 05 21" src="https://user-images.githubusercontent.com/10519803/91554329-975aba00-e937-11ea-9a98-eaff35191197.png">
82-
83-
84-
<details>
85-
<summary>SPOILER: Recommended implementation WSS based on Websocket-kit(Swift-NIO)</summary>
86-
87-
88-
This is propertyWrapper for threadsafe access to webSocket instance
89-
90-
```swift
91-
import Foundation
92-
93-
@propertyWrapper
94-
struct Atomic<Value> {
95-
96-
private var value: Value
97-
private let lock = NSLock()
98-
99-
init(wrappedValue value: Value) {
100-
self.value = value
101-
}
102-
103-
var wrappedValue: Value {
104-
get { return load() }
105-
set { store(newValue: newValue) }
106-
}
107-
108-
func load() -> Value {
109-
lock.lock()
110-
defer { lock.unlock() }
111-
return value
112-
}
113-
114-
mutating func store(newValue: Value) {
115-
lock.lock()
116-
defer { lock.unlock() }
117-
value = newValue
118-
}
119-
}
120-
121-
```
122-
123-
This is implementation WSS
124-
125-
```swift
126-
import NIO
127-
import NIOHTTP1
128-
import NIOWebSocket
129-
import WebSocketKit
130-
131-
final class WSS: ACWebSocketProtocol {
132-
133-
var url: URL
134-
private var eventLoopGroup: EventLoopGroup
135-
@Atomic var ws: WebSocket?
136-
137-
init(stringURL: String, coreCount: Int = System.coreCount) {
138-
url = URL(string: stringURL)!
139-
eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: coreCount)
140-
}
141-
142-
var onConnected: ((_ headers: [String : String]?) -> Void)?
143-
var onDisconnected: ((_ reason: String?) -> Void)?
144-
var onCancelled: (() -> Void)?
145-
var onText: ((_ text: String) -> Void)?
146-
var onBinary: ((_ data: Data) -> Void)?
147-
var onPing: (() -> Void)?
148-
var onPong: (() -> Void)?
149-
150-
func connect(headers: [String : String]?) {
151-
152-
var httpHeaders: HTTPHeaders = .init()
153-
headers?.forEach({ (name, value) in
154-
httpHeaders.add(name: name, value: value)
155-
})
156-
let promise: EventLoopPromise<Void> = eventLoopGroup.next().makePromise(of: Void.self)
157-
158-
WebSocket.connect(to: url.absoluteString,
159-
headers: httpHeaders,
160-
on: eventLoopGroup
161-
) { ws in
162-
self.ws = ws
163-
164-
ws.onPing { [weak self] (ws) in
165-
self?.onPing?()
166-
}
167-
168-
ws.onPong { [weak self] (ws) in
169-
self?.onPong?()
170-
}
171-
172-
ws.onClose.whenComplete { [weak self] (result) in
173-
switch result {
174-
case .success:
175-
self?.onDisconnected?(nil)
176-
self?.onCancelled?()
177-
case let .failure(error):
178-
self?.onDisconnected?(error.localizedDescription)
179-
self?.onCancelled?()
180-
}
181-
}
182-
183-
ws.onText { (ws, text) in
184-
self.onText?(text)
185-
}
186-
187-
ws.onBinary { (ws, buffer) in
188-
var data: Data = Data()
189-
data.append(contentsOf: buffer.readableBytesView)
190-
self.onBinary?(data)
191-
}
192-
193-
}.cascade(to: promise)
194-
195-
promise.futureResult.whenSuccess { [weak self] (_) in
196-
guard let self = self else { return }
197-
self.onConnected?(nil)
198-
}
199-
}
200-
201-
func disconnect() {
202-
ws?.close(promise: nil)
203-
}
204-
205-
func send(data: Data) {
206-
ws?.send([UInt8](data))
207-
}
208-
209-
func send(data: Data, _ completion: (() -> Void)?) {
210-
let promise: EventLoopPromise<Void>? = ws?.eventLoop.next().makePromise(of: Void.self)
211-
ws?.send([UInt8](data), promise: promise)
212-
promise?.futureResult.whenComplete { (_) in
213-
completion?()
214-
}
215-
}
216-
217-
func send(text: String) {
218-
ws?.send(text)
219-
}
220-
221-
func send(text: String, _ completion: (() -> Void)?) {
222-
let promise: EventLoopPromise<Void>? = ws?.eventLoop.next().makePromise(of: Void.self)
223-
ws?.send(text, promise: promise)
224-
promise?.futureResult.whenComplete { (_) in
225-
completion?()
226-
}
227-
}
228-
}
229-
```
230-
</details>
231-
232-
---
233-
234-
### Use with [Starscream](https://github.com/daltoniam/Starscream)
235-
236-
```ruby
237-
pod 'Starscream', '~> 4.0.0'
238-
```
239-
<details>
240-
<summary>SPOILER: If you still want to use "Starscream", then you can to copy this code for websocket client</summary>
241-
242-
```swift
243-
import Foundation
244-
import Starscream
245-
246-
class WSS: ACWebSocketProtocol, WebSocketDelegate {
247-
248-
var url: URL
249-
var ws: WebSocket
250-
251-
init(stringURL: String) {
252-
url = URL(string: stringURL)!
253-
ws = WebSocket(request: URLRequest(url: url))
254-
ws.delegate = self
255-
}
256-
257-
var onConnected: ((_ headers: [String : String]?) -> Void)?
258-
var onDisconnected: ((_ reason: String?) -> Void)?
259-
var onCancelled: (() -> Void)?
260-
var onText: ((_ text: String) -> Void)?
261-
var onBinary: ((_ data: Data) -> Void)?
262-
var onPing: (() -> Void)?
263-
var onPong: (() -> Void)?
264-
265-
func connect(headers: [String : String]?) {
266-
ws.request.allHTTPHeaderFields = headers
267-
ws.connect()
268-
}
269-
270-
func disconnect() {
271-
ws.disconnect()
272-
}
273-
274-
func send(data: Data) {
275-
ws.write(data: data)
276-
}
277-
278-
func send(data: Data, _ completion: (() -> Void)?) {
279-
ws.write(data: data, completion: completion)
280-
}
281-
282-
func send(text: String) {
283-
ws.write(string: text)
284-
}
285-
286-
func send(text: String, _ completion: (() -> Void)?) {
287-
ws.write(string: text, completion: completion)
288-
}
289-
290-
func didReceive(event: WebSocketEvent, client: WebSocket) {
291-
switch event {
292-
case .connected(let headers):
293-
onConnected?(headers)
294-
case .disconnected(let reason, let code):
295-
onDisconnected?(reason)
296-
case .text(let string):
297-
onText?(string)
298-
case .binary(let data):
299-
onBinary?(data)
300-
case .ping(_):
301-
onPing?()
302-
case .pong(_):
303-
onPong?()
304-
case .cancelled:
305-
onCancelled?()
306-
default: break
307-
}
308-
}
309-
}
310-
311-
```
312-
</details>
313-
314-
---
315-
316-
### Next step to use ActionCableSwift
317-
318-
31937
```swift
32038
import ActionCableSwift
32139

@@ -324,7 +42,7 @@ let ws: WSS = .init(stringURL: "ws://localhost:3001/cable")
32442

32543
/// action cable client
32644
let clientOptions: ACClientOptions = .init(debug: false, reconnect: true)
327-
let client: ACClient = .init(ws: ws, options: clientOptions)
45+
let client: ACClient = .init(stringURL: "ws://localhost:3001/cable", options: clientOptions)
32846
/// pass headers to connect
32947
/// on server you can get this with env['HTTP_COOKIE']
33048
client.headers = ["COOKIE": "Value"]
@@ -402,13 +120,23 @@ client.headers = [
402120

403121
---
404122

405-
## Requirements
123+
# If you want to implement your own WebSocket Provider, you should to implement the `ACWebSocketProtocol` protocol and use another initializator for ACClient
406124

407-
Any Web Socket Library, e.g.
125+
```swift
126+
import ActionCableSwift
408127

409-
[Websocket-kit](https://github.com/vapor/websocket-kit)
128+
/// web socket client
129+
let ws: YourWSS = .init(stringURL: "ws://localhost:3001/cable")
130+
131+
/// action cable client
132+
let clientOptions: ACClientOptions = .init(debug: false, reconnect: true)
133+
let client: ACClient = .init(ws: ws, options: clientOptions)
134+
```
410135

411-
[Starscream](https://github.com/daltoniam/Starscream)
136+
---
137+
## Requirements
138+
139+
[Websocket-kit](https://github.com/vapor/websocket-kit)
412140

413141
## Author
414142

0 commit comments

Comments
 (0)