iOS Question [SOLVED] iWebSocket library: can't connect using Janus subProtocol

JordiCP

Expert
Licensed User
Longtime User
Hi,

I'm implementing a webRTC client for a remote device. Successfully implemented all the pushkit+callkit part using inline OBJC.
Now I'm facin the problem that, in order to set-up the connection, I need to open a WebSocket, but as soon as I do it, it closes.
The problem seems to be that the remote device's webSocket uses Janus specific-subprotocol, and it must be specified in the headers when connecting

For reference, the working codes in other languages are
B4X:
'React-Native partial code
const ws = new WebSocket( 'ws://xx.xx.xx.xx:pppp' , 'janus-protocol');

'Swift partial code
let url = URL(string: "ws://xx.xx.xx.xx:pppp)!
var request = URLRequest(url: url)
request.setValue("janus-protocol", forHTTPHeaderField: "Sec-WebSocket-Protocol")
let ws = URLSessionWebSocketTask(session: session, url: url)

I can't figure out how to do it with the iWebSocket library. Perhaps there is some workaround with nativeObject? Otherwise I'll have to implement it with inline OBJC, but of course I'd like to avoid it.
 

JordiCP

Expert
Licensed User
Longtime User
With the help of AI and some needed corrections, I managed to implement my own class that can replace iWebSocket.
Just instantiate the class, and initialize it with the callback to the owner module and chosen event name. When connecting, you can also specify the subProtocol besides the URL
Same events and format as iWebSocket will be raised, so it will work, at least in my case, as a drop-in replacement for iWebSocket. No idea if it will work in other scenarios in which iWebSocket already works properly.

I'm sharing it here in case someone ever needs something similar. Take into account that the name class must be 'clWebSocketExt', since there is an extension inside the OBJC block (or change it accordingly)
B4X:
'clWebSocketExt.bas
Sub Class_Globals
    Private mCallback As Object
    Private mEventName As String
    Private no As NativeObject
End Sub

Public Sub Initialize(cbk As Object, EventName As String)
    mCallback  = cbk
    mEventName = EventName.ToLowerCase
    no = Me
    no.RunMethod("jws_init:", Array("ws"))
End Sub

Public Sub Connect(Url As String, SubProtocol As String)
    no.RunMethod("jws_connect::", Array(Url, SubProtocol))
End Sub

Public Sub SendText(Text As String)
    no.RunMethod("jws_send:", Array(Text))
End Sub

Public Sub Close
    no.RunMethod("jws_close", Null)
End Sub

' Events
Sub ws_connected
    Log("WS connected")
    CallSub(mCallback, mEventName & "_connected")
End Sub

Sub ws_message(Text As String)
    Log(Text)
    CallSub2(mCallback, mEventName & "_TextMessage", Text)
End Sub

Sub ws_closed(Code As Int, Reason As String)
    Log("Closed: " & Code & " " & Reason)
    CallSub2(mCallback, mEventName & "_closed", Reason)
End Sub

Sub ws_error(Message As String)
    Log("Error: " & Message)
    CallSub2(mCallback, mEventName & "_Error", Message)
End Sub




#If OBJC

#import <Foundation/Foundation.h>

 NSURLSession *jws_session;
 NSURLSessionWebSocketTask *jws_task;
 NSString *jws_eventName;
 B4I *jws_bi;
 NSObject *jws_target;

@end
@interface JWSDelegate : NSObject <NSURLSessionWebSocketDelegate>
@end

@implementation JWSDelegate

- (void)URLSession:(NSURLSession *)session
     webSocketTask:(NSURLSessionWebSocketTask *)webSocketTask
 didOpenWithProtocol:(NSString *)protocol {

    NSLog(@"jws_didOpen %@", protocol);   

    [jws_bi raiseEvent:nil
                 event:[NSString stringWithFormat:@"%@_connected", jws_eventName]
                params:nil];
}

- (void)URLSession:(NSURLSession *)session
     webSocketTask:(NSURLSessionWebSocketTask *)webSocketTask
 didCloseWithCode:(NSURLSessionWebSocketCloseCode)closeCode
            reason:(NSData *)reason {

    NSLog(@"jws_didClose %@", (NSInteger)closeCode);   


    NSString *r = @"";
    if (reason != nil) {
        r = [[NSString alloc] initWithData:reason encoding:NSUTF8StringEncoding];
        if (r == nil) r = @"";
    }

    [jws_bi raiseEvent:nil
                 event:[NSString stringWithFormat:@"%@_closed::", jws_eventName]
                params:@[@((NSInteger)closeCode), r]];
}

@end

@implementation b4i_clwebsocketext (a)

 JWSDelegate *delegate;


- (void)jws_init:(NSString *)eventName {
    jws_eventName = eventName;
    jws_bi = self.bi;
    jws_target = self;
}

- (void)jws_connect:(NSString *)urlString :(NSString *)subProtocol {
    NSURL *url = [NSURL URLWithString:urlString];

    NSLog(@"jws_connect");

    delegate = [JWSDelegate new];

    jws_session = [NSURLSession sessionWithConfiguration:NSURLSessionConfiguration.defaultSessionConfiguration
                                                delegate:delegate
                                           delegateQueue:[NSOperationQueue mainQueue]];

    NSArray *protocols = subProtocol.length > 0 ? @[subProtocol] : @[];

    jws_task = [jws_session webSocketTaskWithURL:url protocols:protocols];
   
    NSLog(@"delegate=%@", jws_session.delegate);
    NSLog(@"task=%@", jws_task);
   
   
    [jws_task resume];
    [self jws_receive];
}

- (void)jws_receive {

    NSLog(@"jws_receive");

    if (jws_task == nil) return;

    NSLog(@"jws_receive2");

    [jws_task receiveMessageWithCompletionHandler:^(NSURLSessionWebSocketMessage * _Nullable message, NSError * _Nullable error) {

        if (error != nil) {
            NSLog(@"jws_receive_error");
       
            [jws_bi raiseEvent:nil
                         event:[NSString stringWithFormat:@"%@_error:", jws_eventName]
                        params:@[error.localizedDescription ?: @""]];
            return;
        }

        if (message.type == NSURLSessionWebSocketMessageTypeString) {
            NSLog(@"jws_receive_ok");
            [jws_bi raiseEvent:nil
                         event:[NSString stringWithFormat:@"%@_message:", jws_eventName]
                        params:@[message.string ?: @""]];
        }

        [self jws_receive];
    }];
}

- (void)jws_send:(NSString *)text {

    NSLog(@"jws_send");
    if (jws_task == nil) return;
    NSLog(@"jws_sen2");

    NSURLSessionWebSocketMessage *msg =
        [[NSURLSessionWebSocketMessage alloc] initWithString:text];

    [jws_task sendMessage:msg completionHandler:^(NSError * _Nullable error) {
        if (error != nil) {
            NSLog(@"jws_send_error");
            [jws_bi raiseEvent:nil
                         event:[NSString stringWithFormat:@"%@_error:", jws_eventName]
                        params:@[error.localizedDescription ?: @""]];
        }
        else{
            NSLog(@"jws_send_ok");
        }
    }];
}

- (void)jws_close {
    if (jws_task != nil) {
        NSLog(@"jws_close_will_proceed");   

        [jws_task cancelWithCloseCode:NSURLSessionWebSocketCloseCodeNormalClosure
                               reason:nil];
        jws_task = nil;
    }
    else{
            NSLog(@"jws_close_already_closed");   
    }
}

#End If
 
Upvote 0
Top