Skip to content

Instantly share code, notes, and snippets.

@soltrinox
Created March 18, 2023 02:37
Show Gist options
  • Save soltrinox/b6b0788e381ccd8ea021f544302006ba to your computer and use it in GitHub Desktop.
Save soltrinox/b6b0788e381ccd8ea021f544302006ba to your computer and use it in GitHub Desktop.
SwiftUi secure rest upgrade to websocket if available

COMMAND : write the swiftui code to connect to a secure REST service and upgrade to a secure websocket if available

Here's an example of how to use SwiftUI to connect to a secure REST service and upgrade to a secure WebSocket if available using URLSession and URLSessionWebSocketTask:

import SwiftUI
import Combine

class DataService: ObservableObject {
    private var webSocketTask: URLSessionWebSocketTask?
    private var webSocketPublisher: AnyPublisher<Result<URLSessionWebSocketTask.Message, Error>, Never>?
    @Published var data: String = ""

    func loadData() {
        guard let url = URL(string: "https://example.com/api/data") else { return }

        var request = URLRequest(url: url)
        request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")

        URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
            guard let self = self else { return }

            if let data = data {
                self.data = String(data: data, encoding: .utf8) ?? ""
            } else if let error = error {
                print("Data loading error: \(error.localizedDescription)")
            }

            if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 101 {
                self.upgradeToWebSocket()
            }
        }.resume()
    }

    private func upgradeToWebSocket() {
        guard let url = URL(string: "wss://example.com/ws") else { return }

        var request = URLRequest(url: url)
        request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")

        webSocketTask = URLSession.shared.webSocketTask(with: request)
        webSocketTask?.resume()

        webSocketPublisher = webSocketTask?.receive()
            .map { result in
                switch result {
                case .success(let message):
                    return .success(message)
                case .failure(let error):
                    return .failure(error)
                }
            }
            .replaceError(with: .failure(NSError(domain: "", code: 0, userInfo: nil)))
            .eraseToAnyPublisher()

        webSocketPublisher?
            .sink(receiveValue: { [weak self] result in
                switch result {
                case .success(let message):
                    if let text = try? message.getAsString() {
                        self?.data = text
                    }
                case .failure(let error):
                    print("WebSocket error: \(error.localizedDescription)")
                }
            })
            .store(in: &cancellables)
    }

    private var cancellables = Set<AnyCancellable>()

    func disconnect() {
        webSocketTask?.cancel(with: .normalClosure, reason: nil)
        webSocketTask = nil
        webSocketPublisher = nil
    }
}

struct ContentView: View {
    @StateObject var dataService = DataService()

    var body: some View {
        VStack {
            Text("Data: \(dataService.data)")
                .padding()
            Button(action: {
                dataService.loadData()
            }) {
                Text("Load Data")
            }
            Button(action: {
                dataService.disconnect()
            }) {
                Text("Disconnect")
            }
        }
    }
}

This code defines a DataService class that loads data from a secure REST service using URLSession and upgrades to a secure WebSocket if the response has a 101 status code. The ContentView struct creates an instance of the DataService and provides UI controls to load data and disconnect from the WebSocket. When the WebSocket is connected, messages received from the server are published as an observable object property.

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