Skip to content

Commit b669312

Browse files
committed
rework wonky reconnection logic
1 parent 6461cc0 commit b669312

File tree

1 file changed

+69
-39
lines changed

1 file changed

+69
-39
lines changed

AllSpark-ios/CameraViewController.swift

Lines changed: 69 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ class CameraViewController: UIViewController, UIDocumentPickerDelegate, UINaviga
3535
private var webSocketURL: URL?
3636
private var isConnected = false
3737
private var isSecureProtocol = false
38+
private var isAttemptingConnection = false
39+
private var connectionAttemptTimer: Timer?
3840

3941
// Display layer
4042
private var imageView: UIImageView!
@@ -68,8 +70,8 @@ class CameraViewController: UIViewController, UIDocumentPickerDelegate, UINaviga
6870
self?.captureSession.startRunning()
6971
}
7072

71-
// Reconnect WebSocket if needed
72-
if !isConnected || webSocketTask == nil {
73+
// Reconnect WebSocket if not connected
74+
if !isConnected && !isAttemptingConnection {
7375
setupWebSocketConnection()
7476
}
7577
}
@@ -216,11 +218,19 @@ class CameraViewController: UIViewController, UIDocumentPickerDelegate, UINaviga
216218
private func updateConnectionStatusIcon() {
217219
DispatchQueue.main.async { [weak self] in
218220
if self?.isConnected ?? false {
221+
// Connected - green
219222
if let image = UIImage(systemName: "wifi") {
220223
self?.connectionStatusIcon.setImage(image, for: .normal)
221224
self?.connectionStatusIcon.tintColor = .systemGreen
222225
}
226+
} else if self?.isAttemptingConnection ?? false {
227+
// Attempting connection - amber/orange
228+
if let image = UIImage(systemName: "wifi") {
229+
self?.connectionStatusIcon.setImage(image, for: .normal)
230+
self?.connectionStatusIcon.tintColor = .systemOrange
231+
}
223232
} else {
233+
// Disconnected - red
224234
if let image = UIImage(systemName: "wifi.slash") {
225235
self?.connectionStatusIcon.setImage(image, for: .normal)
226236
self?.connectionStatusIcon.tintColor = .systemRed
@@ -610,6 +620,10 @@ class CameraViewController: UIViewController, UIDocumentPickerDelegate, UINaviga
610620

611621
private func connectWebSocket() {
612622
guard let wsURL = webSocketURL else { return }
623+
guard !isAttemptingConnection else { return } // Prevent multiple simultaneous attempts
624+
625+
isAttemptingConnection = true
626+
updateConnectionStatusIcon()
613627

614628
// Set secure protocol flag based on connection URL
615629
if wsURL.absoluteString.lowercased().hasPrefix("wss://") {
@@ -629,21 +643,13 @@ class CameraViewController: UIViewController, UIDocumentPickerDelegate, UINaviga
629643

630644
print("Connecting to WebSocket at \(wsURL)")
631645

632-
// Start receiving messages - this will detect connection errors
646+
// Start receiving messages - this will detect connection errors and trigger fallback if needed
633647
receiveWebSocketMessage()
634-
635-
// Set a timeout - if not connected after 5 seconds, try fallback
636-
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) { [weak self] in
637-
guard let self = self else { return }
638-
if !self.isConnected {
639-
print("WebSocket connection timeout, attempting fallback...")
640-
self.attemptWsFallback()
641-
}
642-
}
643648
}
644649

645650
private func attemptWsFallback() {
646651
guard let wsURL = webSocketURL else { return }
652+
guard isSecureProtocol else { return } // Only fallback if we were trying WSS
647653

648654
// Convert wss:// to ws://
649655
var wsURLString = wsURL.absoluteString
@@ -654,33 +660,21 @@ class CameraViewController: UIViewController, UIDocumentPickerDelegate, UINaviga
654660
return
655661
}
656662

657-
// Disconnect the current task
663+
// Disconnect the current failed WSS task
658664
if let task = webSocketTask {
659665
task.cancel(with: .goingAway, reason: nil)
660666
}
667+
webSocketTask = nil
668+
isAttemptingConnection = false
661669

662-
let verifyCertificate = UserDefaults.standard.bool(forKey: "verifyCertificate")
663-
let config = URLSessionConfiguration.default
664-
let delegate = CertificateVerificationDelegate(verifyCertificate: verifyCertificate)
665-
let urlSession = URLSession(configuration: config, delegate: delegate, delegateQueue: nil)
666-
let task = urlSession.webSocketTask(with: wsURLFallback)
667-
668-
self.webSocketTask = task
669-
self.webSocketURL = wsURLFallback
670+
// Update the URL to the fallback WS URL
671+
webSocketURL = wsURLFallback
670672
isSecureProtocol = false
671-
task.resume()
672-
673-
print("Connecting to WebSocket fallback at \(wsURLFallback)")
674673

675-
// Start receiving messages
676-
receiveWebSocketMessage()
674+
print("WSS connection failed, attempting WS fallback at \(wsURLFallback)")
677675

678-
// Mark as connected
679-
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
680-
self?.isConnected = true
681-
self?.updateConnectionStatusIcon()
682-
print("WebSocket fallback connected")
683-
}
676+
// Now connect using WS
677+
connectWebSocket()
684678
}
685679

686680
private func disconnectWebSocket() {
@@ -689,6 +683,9 @@ class CameraViewController: UIViewController, UIDocumentPickerDelegate, UINaviga
689683
}
690684
webSocketTask = nil
691685
isConnected = false
686+
isAttemptingConnection = false
687+
connectionAttemptTimer?.invalidate()
688+
connectionAttemptTimer = nil
692689
updateConnectionStatusIcon()
693690
print("WebSocket disconnected")
694691
}
@@ -780,16 +777,22 @@ extension CameraViewController {
780777
private func receiveWebSocketMessage() {
781778
guard let webSocketTask = webSocketTask else { return }
782779

780+
// Set a flag to track if this is the first receive attempt for this connection
781+
let isFirstReceive = !isConnected && isAttemptingConnection
782+
783783
webSocketTask.receive { [weak self] result in
784784
DispatchQueue.main.async {
785785
switch result {
786786
case .success(let message):
787-
// Mark as connected on first successful message
788-
if self?.isConnected == false {
787+
// Mark as connected on first successful receive (connection is established)
788+
if self?.isConnected == false && self?.isAttemptingConnection == true {
789789
self?.isConnected = true
790+
self?.isAttemptingConnection = false
791+
self?.connectionAttemptTimer?.invalidate()
792+
self?.connectionAttemptTimer = nil
790793
self?.updateConnectionStatusIcon()
791794

792-
print("WebSocket connected")
795+
print("WebSocket connection established")
793796
}
794797

795798
switch message {
@@ -829,13 +832,40 @@ extension CameraViewController {
829832
case .failure(let error):
830833
print("WebSocket receive error: \(error)")
831834

832-
// Check if this is a TLS/connection error and we haven't connected yet
833-
if self?.isConnected == false {
835+
// If we haven't connected yet and were using WSS, try the WS fallback
836+
if self?.isConnected == false && self?.isSecureProtocol == true {
834837
let errorString = error.localizedDescription.lowercased()
835-
if errorString.contains("tls") || errorString.contains("certificate") || errorString.contains("refused") {
836-
print("WSS connection failed with TLS error, attempting WS fallback...")
838+
// Check for common secure connection errors
839+
if errorString.contains("tls") || errorString.contains("certificate") || errorString.contains("refused") || errorString.contains("connection") {
840+
print("WSS connection failed, attempting WS fallback...")
841+
self?.connectionAttemptTimer?.invalidate()
842+
self?.connectionAttemptTimer = nil
837843
self?.attemptWsFallback()
838844
}
845+
} else if self?.isConnected == false {
846+
// WS fallback also failed
847+
print("WS fallback connection failed")
848+
self?.connectionAttemptTimer?.invalidate()
849+
self?.connectionAttemptTimer = nil
850+
self?.isAttemptingConnection = false
851+
self?.updateConnectionStatusIcon()
852+
}
853+
}
854+
}
855+
}
856+
857+
// On first receive, set a short timeout to mark connection as established if no early error
858+
if isFirstReceive {
859+
connectionAttemptTimer?.invalidate()
860+
connectionAttemptTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { [weak self] _ in
861+
// If we still haven't connected after 0.5s but no error occurred, mark as connected
862+
if self?.isConnected == false && self?.isAttemptingConnection == true {
863+
DispatchQueue.main.async {
864+
self?.isConnected = true
865+
self?.isAttemptingConnection = false
866+
self?.connectionAttemptTimer = nil
867+
self?.updateConnectionStatusIcon()
868+
print("WebSocket connection established (timeout)")
839869
}
840870
}
841871
}

0 commit comments

Comments
 (0)