0

I am working with a package called DSBridge to connect a mobile application (iOS code in this case) to javascript code containing the main logic of my application. This involves running the JavaScript on an invisible WKWebView.

My JavaScript code needs to call a method in the native iOS application which needs to execute and return a value to the JavaScript.

My Objective C function reads:

- (NSString* )read:(NSDictionary *) args{
    NSLog(@"In Read");
    while(self->connection != NULL) {
        if([self->connection isMessageAvailable]) {
            [self->connection messageRetrieved];
            NSLog(@"Message Received in ViewController");
            return [self->connection getMessage];
        }
    }
    return NULL;
}

Which is called from my JavaScript, I need this to return on the main thread so that the return value is sent into my WebView.

The issue is that my the message from IO I am waiting for will never appear as my NSStream is blocked by the while loop.

My connection methods and NSStream look like this:

-(void)readData:(NSData*)receivedData{
    NSData *decoded = [self btXOR:receivedData withMask:0x26];
    message = [[NSString alloc] initWithData:decoded encoding:NSUTF8StringEncoding];
    NSLog(@"message: %@", message);
    messageAvailable = true;
}

- (BOOL) isMessageAvailable {
    return messageAvailable;
}

- (void) messageRetrieved {
    messageAvailable = false;
}

- (NSString*) getMessage {
    NSLog(@"Message: %@", message);
    return message;
}

#pragma mark StreamDelegate
-(void) stream:(NSStream *)theStream handleEvent:(NSStreamEvent)eventCode
{
    switch (eventCode)
    {

            //Reading in from host done here
            case NSStreamEventHasBytesAvailable:
            DDLogInfo(@"NSStreamEventHasBytesAvailable");
            DDLogInfo(@"stream is input: %i " , (theStream == inputStream));
            NSLog(@"NSStreamEventHasBytesAvailable");
            NSLog(@"stream is input: %i " , (theStream == inputStream));


            if (theStream == inputStream)
        {
            uint8_t buffer[1024];
            long len;

            while ([inputStream hasBytesAvailable])
            {
                len = [inputStream read:buffer maxLength:sizeof(buffer)];
                DDLogDebug(@"InputStream still has bytes");
                NSLog(@"InputStream still has bytes");
                if (len > 0)
                {
                    NSMutableData* data=[[NSMutableData alloc] initWithLength:0];

                    [data appendBytes: (const void *)buffer length:(int)len];
                    [self readData:data];
                }
            }
        }
            break;

The 'NSStreamEventHasBytesAvailable' case should occur once the message is ready, and should be picked up by my read function so it can be returned to my WebViews JavaScript.

How can I take the while loop off of the main thread and still return once I have the message? Is it possible to take the NSStream off the main runloop in a way that it will not be blocked by my read function?

I tried making read async and using a callback to the JavaScript as seen here, but this makes my JavaScript codebase rather unwieldy with callbacks.

saxford
  • 23
  • 5
  • 2
    The only way you can not block the main thread is to make the code asynchronous, which is the right thing to do anyway. A busy-while loop is awful – Paulw11 Jan 14 '19 at 20:59
  • Agreed with Paul. You can't dodge the asynchronous nature of I/O calls. That's why Node is full of callbacks. You can build other solutions based on JavaScript Futures or Promises and go down the React Native route, but eventually you're going to have to grapple with the fact that I/O takes a very long time and you can't just wait for it. (This is one of the reasons that building apps in JavaScript is so complicated. To build a really good app, you need to be an expert in JavaScript *and* an expert in iOS/Cocoa to know how to bridge.) – Rob Napier Jan 14 '19 at 22:43

0 Answers0