4 года назад я уже разбирался с вебсокетами в iOS, тогда я решил задачу с помощью одной из библиотек cocoapods, статья есть на Хабре. А сегодня хочу продемонстрировать еще одно решение, нативное без cocoapods.
Я написал свой менеджер для работы с вебсокетами (Менеджер в данном случае синглтон класс который может быть вызван в любом месте приложения, так как это синглтон его экземпляр будет создан только один раз для всего приложения).
Воспользуюсь я для своих целей нативными средствами.
import Foundation
class WSManager {
public static let shared = WSManager() // создаем Синглтон
private init(){}
private var dataArray = [МОДЕЛЬ_МОИХ_ДАННЫХ]()
let webSocketTask = URLSession(configuration: .default).webSocketTask(with: URL(string: "wss://ТУТ_ВАШ_АДРЕС"*))
//функция вызова подключения
public func connectToWebSocket() {
webSocketTask.resume()
self.receiveData() { _ in }
}
//функция подписки на что либо
public func subscribeBtcUsd() {
let message = URLSessionWebSocketTask.Message.string("SUBSCRIBE: НА_ЧТО_ПОДПИСЫВАЕМСЯ")
webSocketTask.send(message) { error in
if let error = error {
print("WebSocket couldn’t send message because: \(error)")
}
}
}
//функция отписки от чего либо
public func unSubscribeBtcUsd() {
let message = URLSessionWebSocketTask.Message.string("UNSUBSCRIBE: ОТ_ЧЕГО_ОТПИСЫВАЕМСЯ ")
webSocketTask.send(message) { error in
if let error = error {
print("WebSocket couldn’t send message because: \(error)")
}
}
}
//функция получения данных, с эскейпингом чтобы получить данные наружу
func receiveData(completion: @escaping ([МОДЕЛЬ_МОИХ_ДАННЫХ]?) -> Void) {
webSocketTask.receive { result in
switch result {
case .failure(let error):
print("Error in receiving message: \(error)")
case .success(let message):
switch message {
case .string(let text):
let data: Data? = text.data(using: .utf8)
let srvData = try? CODABLE_МОДЕЛЬ_ТОГО_ЧТО_ДОЛЖНО_ПРИЙТИ.decode(from: data ?? Data())
for singleData in srvData ?? [] {
self.dataArray.append(МОДЕЛЬ_МОИХ_ДАННЫХ(параметр1: singleData.parametr1, параметр2: singleData.parametr2, параметр3: singleData.parametr3))
}
case .data(let data):
// В вашем варианте данные могут приходить сразу сюда
print("Received data: \(data)")
@unknown default:
debugPrint("Unknown message")
}
self.receiveData() {_ in } // рекурсия
}
}
completion(self.dataArray) // отправляем в комплишн то что насобирали в нашу модель
}
}
Вот такой менеджер получился, пример вызова
import UIKit
class MainViewController: UIViewController {
private var dataArray = [МОДЕЛЬ_МОИХ_ДАННЫХ]()
override func viewDidLoad() {
super.viewDidLoad()
WSManager.shared.connectToWebSocket() // подключаемся
WSManager.shared.subscribeBtcUsd() //подписываемся на получение данных
self.getData() //получаем данные
}
private func getData() {
//получаем данные
WSManager.shared.receiveData() { [weak self] (data) in
guard let self = self else { return }
guard let data = data else { return }
self.dataArray = data // кладем данные в переменную и дальше можно делать с ними то что требуется
}
}
}
*по поводу адреса
wss:// это аналог https://
ws:// это аналог http://
Вот такой вариант работы с вебсокетом получился, если есть вопросы, пожелания, поучения как сделать лучше, пишите, буду рад :)
Тестовый пример доступен у меня в гитхабе
Также я использую extension для Decodable который доступен тоже у меня в гитхабе
я про вот эту часть
let srvData = try? CODABLE_МОДЕЛЬ_ТОГО_ЧТО_ДОЛЖНО_ПРИЙТИ.decode(from: data ?? Data())
shai_hulud
Я конечно
не сварщикне swift программист, но хотел поинтересоваться, тестируют ли программы на Swift?И как корректно тестировать статик синглтоны? Может на swift есть какая-то магическая изоляция глобального публичного состоятия?
aposnov Автор
я пока в тестах неочень, возможно смогу чуть позже более развернуто ответить) спустя 3-6 месяцев) но пока такой задачи нет. Но вообще первое что гуглится stackoverflow.com/questions/8256989/singleton-and-unit-testing и medium.com/@martinrybak/how-to-mock-singletons-and-static-methods-in-unit-tests-cbe915933c7d буду смотреть в ту сторону если понадобится
artemvkepke
Другой вопрос — это тестирование кода, в котором происходит работа с синглтоном. Но опять же, если синглтон инъектить как зависимость закрытую протоколом, то класс, в который синглон инъектирован, тестируется так же как обычно.
То есть с точки зрения тестируемости, синглтон может быть вполне безвреден.
Что касается — тут, да, код выглядит довольно опасным. Все места в программе работающие с синглтоном будут работать с общим проперти dataArray, и если пользователь зачем-то начнёт работать с этим синглтоном из разных потоков, то начнется гонка за dataArray. Результатом станет емнип неопределенное поведение.