Skip to content

Instantly share code, notes, and snippets.

@saroar
Last active February 1, 2023 08:26
Show Gist options
  • Save saroar/10784126e6e92f43e80c64a7ee0415a3 to your computer and use it in GitHub Desktop.
Save saroar/10784126e6e92f43e80c64a7ee0415a3 to your computer and use it in GitHub Desktop.
import Foundation
import Combine
import SwiftUI
import FuncNetworking
import AddaMeModels
import KeychainService
import InfoPlist
public struct WebsocketClient {
public typealias HandShake = () -> ()
public typealias OnReceive = (_ incomig: Result<URLSessionWebSocketTask.Message, Error>) -> ()
public typealias Send = (ChatMessageResponse.Item, String) -> ()
public typealias OnConnect = () -> ()
public typealias Disconnect = () -> ()
public typealias HandleData = (_ data: Data) -> ()
public typealias HandleConversationResponse = (_ lastMessage: ChatMessageResponse.Item) -> ()
public typealias HandleMessageResponse = (_ message: ChatMessageResponse.Item) -> ()
public let handshake: HandShake
public let onReceive: OnReceive
public let send: Send
public let onConnect: OnConnect
public let disconnect: Disconnect
public let handleData: HandleData
public let handleConversationResponse: HandleConversationResponse
public let handleMessageResponse: HandleMessageResponse
private let urlSession = URLSession(configuration: .default)
public var socket: URLSessionWebSocketTask!
public init(
handshake: @escaping HandShake,
onReceive: @escaping OnReceive,
send: @escaping Send,
onConnect: @escaping OnConnect,
disconnect: @escaping Disconnect,
handleData: @escaping HandleData,
handleConversationResponse: @escaping HandleConversationResponse,
handleMessageResponse: @escaping HandleMessageResponse
) {
self.handshake = handshake
self.onReceive = onReceive
self.send = send
self.onConnect = onConnect
self.disconnect = disconnect
self.handleData = handleData
self.handleConversationResponse = handleConversationResponse
self.handleMessageResponse = handleMessageResponse
}
}
func token() -> AnyPublisher<String, HTTPError> {
guard let token: AuthTokenResponse = KeychainService.loadCodable(for: .token) else {
print(#line, "not Authorized Token are missing")
return Fail(error: HTTPError.missingTokenFromIOS )
.eraseToAnyPublisher()
}
return Just(token.accessToken)
.setFailureType(to: HTTPError.self)
.eraseToAnyPublisher()
}
public class WebSocketAPI {
public var conversations: AnyPublisher<ConversationResponse.Item, Never> {
return conversationsSubject.eraseToAnyPublisher()
}
public var messages: AnyPublisher<ChatMessageResponse.Item, Never> {
return messagesSubject.eraseToAnyPublisher()
}
private var conversationsSubject = PassthroughSubject<ConversationResponse.Item, Never>()
private var messagesSubject = PassthroughSubject<ChatMessageResponse.Item, Never>()
public var urlSession = URLSession(configuration: .default)
public var socket: URLSessionWebSocketTask!
private var url = EnvironmentKeys.webSocketURL
public init() {}
public func stop() {
socket.cancel(with: .goingAway, reason: nil)
}
public func disconnect() {
socket.cancel(with: .normalClosure, reason: nil)
}
public func handshake() {
guard let token: AuthTokenResponse = KeychainService.loadCodable(for: .token) else {
print(#line, "not Authorized Token are missing")
return
}
var request = URLRequest(url: url)
request.addValue(
"Bearer \(token)",
forHTTPHeaderField: "Authorization"
)
self.socket = urlSession.webSocketTask(with: request)
socket.receive(completionHandler: onReceive)
socket.resume()
onConnect()
}
public func onConnect() {
guard let currentUSER: User = KeychainService.loadCodable(for: .user) else {
return
}
let onconnect = ChatOutGoingEvent.connect(currentUSER).jsonString
socket.send(.string(onconnect!)) { error in
if let error = error {
print(#line, "Error sending message", error)
}
}
}
public func onReceive(_ incoming: Result<URLSessionWebSocketTask.Message, Error>) {
socket.receive(completionHandler: onReceive)
self.socket.receive { result in
switch result {
case .success(let message):
switch message {
case .data(let data):
print(#line, data)
case .string(let str):
print(#line, str)
guard let data = str.data(using: .utf8) else { return }
self.handle(data)
@unknown default:
break
}
case .failure(let error):
print(#line, error)
self.socket.cancel(with: .goingAway, reason: nil)
// self.handshake()
return
}
}
}
public func send(
localMessage: ChatMessageResponse.Item,
remoteMessage: String
) {
self.socket.send(.string(remoteMessage)) { [weak self] error in
// if let error = error {
// print("Error sending message", error)
// }
guard error != nil else {
print(#line, "cant send remote msg something wrong!")
return
}
self?.messagesSubject.send(localMessage)
}
}
public func handle(_ data: Data) {
let chatOutGoingEvent = ChatOutGoingEvent.decode(data: data)
switch chatOutGoingEvent {
case .connect(_):
break
case .disconnect(_):
break
case .conversation(let conversation):
print(#line, conversation)
self.handleConversationResponse(conversation)
case .message(let message):
print(#line, message)
self.handleMessageResponse(message)
case .notice(let msg):
print(#line, msg)
case .error(let error):
print(#line, error)
case .none:
print(#line, "decode error")
}
}
public func handleConversationResponse(_ lastMessage: ChatMessageResponse.Item) {
self.messagesSubject.send(lastMessage)
}
public func handleMessageResponse(_ message: ChatMessageResponse.Item) {
self.messagesSubject.send(message)
}
}
extension WebsocketClient {
public static func live(api: WebSocketAPI) -> Self {
.init(
handshake: api.handshake,
onReceive: api.onReceive(_:),
send: api.send(localMessage:remoteMessage:),
onConnect: api.onConnect,
disconnect: api.disconnect,
handleData: api.handle(_:),
handleConversationResponse: api.handleConversationResponse(_:),
handleMessageResponse: api.handleMessageResponse(_:))
}
}
//ViewModel
class SocketViewModel: ObservableObject {
@Published var conversations = [String: ConversationResponse.Item]()
@Published var messages: [String: [ChatMessageResponse.Item]] = [:]
let websocketClient: WebsocketClient
init(websocketClient: WebsocketClient) {
self.websocketClient = websocketClient
self.websocketClient.handshake()
}
}
@bubudrc
Copy link

bubudrc commented May 11, 2021

on your method:

public func send(
    localMessage: ChatMessageResponse.Item,
    remoteMessage: String
  ) {
      
 ...
  }

the guard is wrong, should be guard error == nil else {...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment