iOS native app, integrating with Cordova 2.9.0 - javascript

I have created a native iOS app (Xcode 5.1), and I want to open via a btn, a cordova(Cordova 2.9.0) web view(Otherwise CDVViewController). I've succeed this and the web view works and it shows me the webpage, but when I embed the cordova.js (inside the webpage), the
CDVCommandQueue.m
- (void)fetchCommandsFromJs
{
// Grab all the queued commands from the JS side.
NSString* queuedCommandsJSON = [_viewController.webView stringByEvaluatingJavaScriptFromString:
#"cordova.require('cordova/exec').nativeFetchMessages()"];
NSLog(#"---- %#",queuedCommandsJSON);
[self enqueCommandBatch:queuedCommandsJSON];
if ([queuedCommandsJSON length] > 0) {
CDV_EXEC_LOG(#"Exec: Retrieved new exec messages by request.");
}
}
calls the above function and it executes the 'cordova.require('cordova/exec').nativeFetchMessages()',
this function returns
[["Device748313476","Device","getDeviceInfo",[]],["NetworkStatus748313477","NetworkStatus","getConnectionInfo",[]]]
and then it passes this value to
- (void)executePending
{
// Make us re-entrant-safe.
if (_currentlyExecuting) {
return;
}
#try {
_currentlyExecuting = YES;
for (NSUInteger i = 0; i < [_queue count]; ++i) {
// Parse the returned JSON array.
NSLog(#"%#",[_queue objectAtIndex:i]);
**NSArray* commandBatch = [[_queue objectAtIndex:i] JSONObject];**
// Iterate over and execute all of the commands.
for (NSArray* jsonEntry in commandBatch) {
CDVInvokedUrlCommand* command = [CDVInvokedUrlCommand commandFromJson:jsonEntry];
CDV_EXEC_LOG(#"Exec(%#): Calling %#.%#", command.callbackId, command.className, command.methodName);
if (![self execute:command]) {
#ifdef DEBUG
NSString* commandJson = [jsonEntry JSONString];
static NSUInteger maxLogLength = 1024;
NSString* commandString = ([commandJson length] > maxLogLength) ?
[NSString stringWithFormat:#"%#[...]", [commandJson substringToIndex:maxLogLength]] :
commandJson;
DLog(#"FAILED pluginJSON = %#", commandString);
#endif
}
}
}
[_queue removeAllObjects];
} #finally
{
_currentlyExecuting = NO;
}
}
My app is crashing because on this line
NSArray* commandBatch = [[_queue objectAtIndex:i] JSONObject];
doesn't recognize the value as json object and it gives me this error message
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFString JSONObject]: unrecognized selector sent to instance
Thanks a lot.

Had the same recently, while was building old cordova app in new Xcode.
You should check Other linker flags in Your target settings:
For the debug build configuration You could use -ObjC flag. (What does the -ObjC linker flag do?, Why do I get a runtime exception of "selector not recognized" when linking against an Objective-C static library that contains categories?)
If after reading previous links, You still want to use this flag in release — just do it.
Otherwise, You should add -force_load ${BUILT_PRODUCTS_DIR}/libCordova.a to the release linker flag.
In order to check/edit active build configuration go to Product > Scheme > Edit scheme (Cmd <).

Related

Cannot upload file to localhost with Scala and JS

I'm trying to run an open-source tool that uses sbt on localhost (9000). However, when I try to upload a file (.arrf) to the tool, which is the first thing required by the tool, I get a FileNotFound error as in the following image.
I have no experience with Scala and have limited experience with JS but as far as I understand there is an issue when uploading the file to the server. I tried with files of different sizes and with files used by the original author. I looked into the code and file upload is handled by a FileUploadService.scala class which is as the following,
class FileUploadService(serviceSavePath: String) {
val basePath = if (serviceSavePath.endsWith("/")) {
serviceSavePath
} else { serviceSavePath + "/" }
val uploadedParts: ConcurrentMap[String, Set[FileUploadInfo]] = new ConcurrentHashMap(8, 0.9f, 1)
def fileNameFor(fileInfo: FileUploadInfo) = {
s"${basePath}${fileInfo.resumableIdentifier}-${fileInfo.resumableFilename}"
}
def isLast(fileInfo: FileUploadInfo): Boolean = {
(fileInfo.resumableTotalSize - (fileInfo.resumableChunkSize * fileInfo.resumableChunkNumber)) < fileInfo.resumableChunkSize
}
def savePartialFile(filePart: Array[Byte], fileInfo: FileUploadInfo) {
if (filePart.length != fileInfo.resumableChunkSize & !isLast(fileInfo)) {
println("error uploading part")
return
}
val partialFile = new RandomAccessFile(fileNameFor(fileInfo), "rw")
val offset = (fileInfo.resumableChunkNumber - 1) * fileInfo.resumableChunkSize
try {
partialFile.seek(offset)
partialFile.write(filePart, 0, filePart.length)
} finally {
partialFile.close()
}
val key = fileNameFor(fileInfo)
if (uploadedParts.containsKey(key)) {
val partsUploaded = uploadedParts.get(key)
uploadedParts.put(key, partsUploaded + fileInfo)
} else {
uploadedParts.put(key, Set(fileInfo))
}
}
As far as I understand from the error, the error occurs in the savePartialFile function where it tries to create a new Random Access File. Here are the details of the last GET request before the error occurs. I also added the error log below. I added quite a lot of outputs and details because I'm very inexperienced in scala and web dev, yet I hope everything is clear.
Cheers!
2022-01-05 13:56:15,972 [ERROR] from application in application-akka.actor.default-dispatcher-86 -
! #7m99nng77 - Internal server error, for (POST) [/upload?resumableChunkNumber=3&resumableChunkSize=1048576&resumableCurrentChunkSize=1048576&resumableTotalSize=17296294&resumableType=&resumableIdentifier=17296294-mixedDriftarff&resumableFilename=mixedDrift.arff&resumableRelativePath=mixedDrift.arff&resumableTotalChunks=16] ->
play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[FileNotFoundException: .\tmp\arff\17296294-mixedDriftarff-mixedDrift.arff (The system cannot find the path specified)]]
at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:255)
at play.api.http.DefaultHttpErrorHandler.onServerError(HttpErrorHandler.scala:182)
at play.core.server.AkkaHttpServer$$anonfun$$nestedInanonfun$executeHandler$1$1.applyOrElse(AkkaHttpServer.scala:230)
at play.core.server.AkkaHttpServer$$anonfun$$nestedInanonfun$executeHandler$1$1.applyOrElse(AkkaHttpServer.scala:229)
at scala.concurrent.Future.$anonfun$recoverWith$1(Future.scala:412)
at scala.concurrent.impl.Promise.$anonfun$transformWith$1(Promise.scala:37)
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:60)
at play.api.libs.streams.Execution$trampoline$.executeScheduled(Execution.scala:109)
at play.api.libs.streams.Execution$trampoline$.execute(Execution.scala:71)
at scala.concurrent.impl.CallbackRunnable.executeWithValue(Promise.scala:68)
at scala.concurrent.impl.Promise$DefaultPromise.$anonfun$tryComplete$1(Promise.scala:284)
at scala.concurrent.impl.Promise$DefaultPromise.$anonfun$tryComplete$1$adapted(Promise.scala:284)
at scala.concurrent.impl.Promise$DefaultPromise.tryComplete(Promise.scala:284)
at scala.concurrent.Promise.complete(Promise.scala:49)
at scala.concurrent.Promise.complete$(Promise.scala:48)
at scala.concurrent.impl.Promise$DefaultPromise.complete(Promise.scala:183)
at scala.concurrent.Promise.failure(Promise.scala:100)
at scala.concurrent.Promise.failure$(Promise.scala:100)
at scala.concurrent.impl.Promise$DefaultPromise.failure(Promise.scala:183)
at scala.concurrent.impl.Promise.$anonfun$transformWith$1(Promise.scala:41)
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:60)
at akka.dispatch.BatchingExecutor$AbstractBatch.processBatch(BatchingExecutor.scala:55)
at akka.dispatch.BatchingExecutor$BlockableBatch.$anonfun$run$1(BatchingExecutor.scala:91)
at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:12)
at scala.concurrent.BlockContext$.withBlockContext(BlockContext.scala:81)
at akka.dispatch.BatchingExecutor$BlockableBatch.run(BatchingExecutor.scala:91)
at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:38)
at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(ForkJoinExecutorConfigurator.scala:43)
at akka.dispatch.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at akka.dispatch.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at akka.dispatch.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at akka.dispatch.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
Caused by: java.io.FileNotFoundException: .\tmp\arff\17296294-mixedDriftarff-mixedDrift.arff (The system cannot find the path specified)
at java.io.RandomAccessFile.open0(Native Method)
at java.io.RandomAccessFile.open(RandomAccessFile.java:316)
at java.io.RandomAccessFile.<init>(RandomAccessFile.java:243)
at java.io.RandomAccessFile.<init>(RandomAccessFile.java:124)
at services.FileUploadService.savePartialFile(FileUploadService.scala:30)
at controllers.OverviewController.$anonfun$upload$3(OverviewController.scala:161)
at play.api.data.Form.fold(Form.scala:144)
at controllers.OverviewController.$anonfun$upload$1(OverviewController.scala:156)
at scala.Function1.$anonfun$andThen$1(Function1.scala:52)
at play.api.mvc.ActionBuilderImpl.invokeBlock(Action.scala:482)
at play.api.mvc.ActionBuilderImpl.invokeBlock(Action.scala:480)
at play.api.mvc.ActionBuilder$$anon$9.invokeBlock(Action.scala:331)
at play.api.mvc.ActionBuilder$$anon$9.invokeBlock(Action.scala:326)
at play.api.mvc.ActionBuilder$$anon$2.apply(Action.scala:419)
at play.api.mvc.Action.$anonfun$apply$2(Action.scala:96)
at scala.concurrent.Future.$anonfun$flatMap$1(Future.scala:302)
at scala.concurrent.impl.Promise.$anonfun$transformWith$1(Promise.scala:37)
... 12 common frames omitted
Apparently, the author of the code is not creating folders programmatically in the code. So creating two empty directories as "tmp/arrf" in the base directory of the sbt project worked.

Mirth Connect: Javascript Reader - Error listing files in dir (shared folder)

I have a trouble when accessing domain's shared folder by Javascript Reader.
it was still running fine until one day it got an error(as below) at catch block.
I restarted the Mirth Service and it works again.
It repeated that many times.
Does anyone know the cause and how to fix it?
Thank you!
My code and the error are as below
Code:
try {
var username = sharedAuth.username;
// If domain exist, login by domain
if (!isEmpty(sharedAuth.domain)) {
username = sharedAuth.domain+'#'+sharedAuth.username;
}
// Domain user format: DOMAIN#user
var options = new com.mirth.connect.connectors.file.FileSystemConnectionOptions(false, username, sharedAuth.password, null);
//logger.debug('=============222====='+username);
var fileConn = new com.mirth.connect.connectors.file.filesystems.SmbFileConnection(sharedHost, options, 5000);
//logger.debug('=============333====='+fileConn);
var arrayFileList = [];
if (fileConn != null) {
var fileList = fileConn.listFiles(sharedPath, '^(ORM|PHY|MDM|ADT|ORU-RPT)_\\w{2}_\\d{14,100}.xml$', true, false);
...
}
} catch (e) {
logger.error('[Source] Error: '+e.message);
logger.error(e);
} finally {
fileConn.destroy();
}
Error:
ERROR 2020-09-24 10:25:31,030 [JavaScript Reader JavaScript Task on ProcessHISOutputXML_SMB_v3_WOR (71600861-ed6f-4004-a8b3-cc7d059f6f5c) < pool-1-thread-1] ProcessHISOutputXML_SMB_v3_WOR-js-connector: [Source] Error: com.mirth.connect.connectors.file.FileConnectorException: Error listing files in dir [his_ris] for pattern [^(ORM|PHY|MDM|ADT|ORU-RPT)_\w{2}_\d{14,100}.xml$]
ERROR 2020-09-24 10:25:31,031 [JavaScript Reader JavaScript Task on ProcessHISOutputXML_SMB_v3_WOR (71600861-ed6f-4004-a8b3-cc7d059f6f5c) < pool-1-thread-1] ProcessHISOutputXML_SMB_v3_WOR-js-connector: JavaException: com.mirth.connect.connectors.file.FileConnectorException: Error listing files in dir [his_ris] for pattern [^(ORM|PHY|MDM|ADT|ORU-RPT)_\w{2}_\d{14,100}.xml$]
What release of Mirth are you on? There was a bug in 3.9.0 that caused memory leaks in the SMB reader library, see https://github.com/nextgenhealthcare/connect/issues/4387.

Determine if software is installed and what version on Mac in Node.js JavaScript

I am in a node environment on Mac within a Electron application and I am needing to:
test if Photoshop is installed
get version of installed Photoshop
launch Photoshop
This was all extremely easy to do in windows. I branch on nodejs os modules platform method so if 'Darwin' I need to do the above things.
I am not a Mac user so I do not know much about the processes on Mac.
I can parse .plist files if need be but poking around users lib preference folder hasn't showed up much. There are Photoshop specific .psp preference files but I have no way to see whats inside them and merely checking to see if there is a Photoshop file located in folder seems way to sloppy to me plus I need to get the version.
Solution
After some research I came across the mac system profiler utility which seems to exist on all mac operating systems.
using node's exec module I get all installed applications and some details about each that look like
TextEdit:
Version: 1.13
Obtained from: Apple
Last Modified: 6/29/18, 11:19 AM
Kind: Intel
64-Bit (Intel): Yes
Signed by: Software Signing, Apple Code Signing Certification Authority, Apple Root CA
Location: /Applications/TextEdit.app
now I just needed to write a simple parser to parse the results which were large with over 335 applications into json for easy querying.
import { exec } from 'child_process';
let proc = exec( 'system_profiler SPApplicationsDataType -detailLevel mini' );
let results = '';
proc.stdout.on( 'data', ( data ) => { results += `${ data }`; } );
proc.on( 'close', async ( code ) =>
{
let parsed = await this.sysProfileTxtToJson( results );
} );
the sysProfileTxtToJson is my little parsing method
now parsed is a json object that I query to determine if photoshop is installed and if multiple version which is the latest version.
here is the parsing method of which needs improved
sysProfileTxtToJson ( data: string )
{
return new Promise<any>( ( res ) =>
{
let stream = new Readable();
stream.push( data );
stream.push( null );
let lineReader = createInterface( stream );
let apps = { Applications: [] };
let lastEntry = '';
let appPrefix = ' ';
let appPropertyPrefix = ' ';
let lastToggle, props = false;
lineReader.on( 'line', ( line: string ) =>
{
if ( line == '' && !lastToggle )
{
props = false;
return;
}
if ( line.startsWith( appPrefix ) && !props )
{
lastEntry = line.trim().replace( ':', '' );
lastToggle = true;
let current = {};
current[ "ApplicationName" ] = lastEntry
apps.Applications.push( current );
props = true;
return;
}
if ( line.startsWith( appPropertyPrefix ) && props )
{
lastToggle = false;
let tokens = line.trim().split( ':' );
let last = apps.Applications[ apps.Applications.length - 1 ];
last[ tokens[ 0 ] ] = tokens[ 1 ].trim();
}
} );
lineReader.on( 'close', () =>
{
res( apps );
} );
} );
}
There are several features in AppleScript which can be utilized to achieve your requirement. Consider shelling out the necessary AppleScript/osascript commands via nodejs.
Let's firstly take a look at the pertinent AppleScript commands...
AppleScript snippets:
The following AppleScript snippet returns the name of whichever version of Photoshop is installed (E.g. Photoshop CS5, Photoshop CS6, Photoshop CC, etc ...). We'll need the name to be able to successfully launch the application.
tell application "Finder" to get displayed name of application file id "com.adobe.Photoshop"
Note: The snippet above errors if Photoshop is not installed, so we can also utilize this to determine if the application is installed or not.
The following snippet obtains whichever version of Photoshop is installed:
tell application "Finder" to get version of application file id "com.adobe.Photoshop"
This returns a long String indicating the version. It will be something like this fictitious example:
19.0.1 (19.0.1x20180407 [20180407.r.1265 2018/04/12:00:00:00) © 1990-2018 Adobe Systems Incorporated
Launching Photoshop:
After it has been inferred that PhotoShop is installed consider utilizing Bash's open command to launch the application. For instance:
open -a "Adobe Photoshop CC"
Example node application:
The following gist demonstrates how the aforementioned commands can be utilized in node.
Note: The gist below is utilizing shelljs's exec command to execute the AppleScript/osascript commands. However you could utilize nodes builtin child_process.execSync() or child_process.exec() instead.
const os = require('os');
const { exec } = require('shelljs');
const APP_REF = 'com.adobe.Photoshop';
const isMacOs = os.platform() === 'darwin';
/**
* Helper function to shell out various commands.
* #returns {String} The result of the cmd minus the newline character.
*/
function shellOut(cmd) {
return exec(cmd, { silent: true }).stdout.replace(/\n$/, '');
}
if (isMacOs) {
const appName = shellOut(`osascript -e 'tell application "Finder" \
to get displayed name of application file id "${APP_REF}"'`);
if (appName) {
const version = shellOut(`osascript -e 'tell application "Finder" \
to get version of application file id "${APP_REF}"'`).split(' ')[0];
console.log(version); // Log the version to console.
shellOut(`open -a "${appName}"`); // Launch the application.
} else {
console.log('Photoshop is not installed');
}
}

Need help to get this code to console application

I have a JavaScript code that I need to get into console application. The script works fine trough cmd but I want to make a console application out of it so its more user friendly. Can someone explain to me how I can write this code in console application or attach it inside the console application with links. I am new to console application so I apologize if I ask anything stupid :-)
When I use it trough cmd then I do the following;
- Run cmd.
- Type "cd downloads" and press enter.
- Type "cscript /nologo process.js log.txt 100 200" and press enter.
- Then I will get a list in the cmd window and I need to have process.js and log.txt in the download folder to make this work.
if(WScript.Arguments.Count() < 3)
{
WScript.Echo("Usage: cscript process.js <filename> <lower_value> <upper_value>");
WScript.Quit();
}
var filename = WScript.Arguments.Item(0);
var lowerBound = parseInt(WScript.Arguments.Item(1));
var upperBound = parseInt(WScript.Arguments.Item(2));
WScript.Echo("Here is the data from the file associated with the text 'verdi', where the");
WScript.Echo("number following 'verdi' is above " + lowerBound + " and below " + upperBound);
var fso = new ActiveXObject("Scripting.FileSystemObject")
var file = fso.OpenTextFile("log.txt", 1, false);
var lines = file.ReadAll().split('\r');
var failed = 0;
for(var idx in lines)
{
try
{
if(lines[idx].indexOf('verdi') > 0)
{
var tmp = lines[idx];
var regex = /verdi\s*\=\s*(\d+)/;
var result = regex.exec(tmp);
var num = parseInt(result[1]);
if(num >= lowerBound && num <= upperBound)
{
WScript.Echo(num);
}
}
}
catch(ex)
{
failed++;
}
}
if(failed > 0)
{
WScript.Echo("WARNING: one or more lines could not be processed!");
}
I have made this code in console application but it doesent work properly. I can choose the values and get the cmd to run. But I don't get the results in the window and print the result to a document.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication3
{
class Program
{
private static object cmd;
private static int verdi;
private static int s;
private static int d;
public static object WScript { get; private set; }
static void Main(string[] args)
{
//Choose lower and upper value
Console.WriteLine("Choose a lower and upper value:");
string value = Console.ReadLine();
//Choose file
Console.WriteLine("Choose a file to scan:");
string file = Console.ReadLine();
//Run the javascript code
Console.WriteLine("cd downloads");
Console.WriteLine("cscript /nologo process.js {0} {1} > mydata.txt", file, value);
string command = Console.ReadLine();
Console.WriteLine("Press any key to start scan");
System.Diagnostics.Process.Start("cmd.exe", "/C" + command);
//Quit Console Application
Console.WriteLine("Press any key to quit.");
Console.ReadKey();
}
}
}
Console.WriteLine only prints strings. It doesn't allow you to execute commands.
You could try this instead:
string command = $"cscript /nologo c:/downloads/process.js c:/downloads/{file} {lowerValue} {upperValue} > mydata.txt");
System.Diagnostics.Process.Start($"cmd.exe /C {command}");
There is also an error in process.js. That script always reads from log.txt and ignores the filename.
But why are you using two programs here? You could just have all the code in one file. And why use JavaScript for one and C# for the other?

JavaScriptCore console.log

I've put together a very simple program that uses JavaScriptCore to evaluate JS:
#import <CoreFoundation/CoreFoundation.h>
#import <JavaScriptCore/JavaScriptCore.h>
int main(int argc, const char * argv[])
{
JSGlobalContextRef ctx = JSGlobalContextCreate(NULL);
FILE *f = fopen(argv[1],"r");
char * buffer = malloc(10000000);
fread(buffer,1,10000000,f);
CFStringRef strs = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingASCII);
JSStringRef jsstr = JSStringCreateWithCFString(strs);
JSValueRef result = JSEvaluateScript(ctx, jsstr, NULL, NULL, 0, NULL);
double res = JSValueToNumber(ctx, result, NULL);
JSGlobalContextRelease(ctx);
printf("%lf\n", res);
return 0;
}
The idea here is that the last value is expected to be a Number, and that value is printed. This works for valid javascript code, such as
var square = function(x) { return x*x; }; square(4)
However, if the code tries to perform a console.log, the program segfaults. Is there a log function available in JSC or do I have to roll my own?
You do have to provide your own console log if using the JavaScriptCore framework from Mac or IOS.
Here is some code that worked for me (sorry it is Objective-C rather than standard C as per your code above):
JSContext *javascriptContext = [[JSContext alloc] init];
javascriptContext[#"consoleLog"] = ^(NSString *message) {
NSLog(#"Javascript log: %#",message);
};
Then you use it from Javascript by:
consoleLog("My debug message");
Note that I have tried to define a vararg version (log taking multiple parameters) but I couldn't get this to work correctly across the framework api.
Note that this solution uses features introduced with the new Objective-C API for the JavaScriptCore.framework introduced at the same time as IOS 7. If you are looking for an intro to this well-integrated bridge between Objective-C and Javascript, check out the 2013 WWDC introduction "Integrating JavaScript into Native Apps" session on Apple's developer network: https://developer.apple.com/videos/wwdc/2013/?id=615
Update to answer:
For those of you wanting to maximise your javascript code reuse without refactoring, I've managed to get a version working that declares a log of the form console.log() :
JSContext *javascriptContext = [[JSContext alloc] init];
[javascriptContext evaluateScript:#"var console = {}"];
javascriptContext[#"console"][#"log"] = ^(NSString *message) {
NSLog(#"Javascript log: %#",message);
};
Then you use it from Javascript by:
console.log("My debug message");
Swift 3.0
let javascriptContext = JSContext()
javascriptContext?.evaluateScript("var console = { log: function(message) { _consoleLog(message) } }")
let consoleLog: #convention(block) (String) -> Void = { message in
print("console.log: " + message)
}
javascriptContext?.setObject(unsafeBitCast(consoleLog, to: AnyObject.self), forKeyedSubscript: "_consoleLog" as (NSCopying & NSObjectProtocol)!)
Swift 2.1
let javascriptContext = JSContext()
javascriptContext.evaluateScript("var console = { log: function(message) { _consoleLog(message) } }")
let consoleLog: #convention(block) String -> Void = { message in
print("console.log: " + message)
}
javascriptContext.setObject(unsafeBitCast(consoleLog, AnyObject.self), forKeyedSubscript: "_consoleLog")
Then you use it from Javascript by:
console.log("My debug message");
self.jsContext = JSContext()
self.jsContext.evaluateScript(...)
let logFunction: #convention(block) (String) -> Void = { (string: String) in
print(string)
}
self.jsContext.setObject(logFunction, forKeyedSubscript: "consoleLog" as NSCopying & NSObjectProtocol)
You can debug JS file attached to context in Safari.
Steps:
1) Start Safari
2) In Safari, enable the Develop menu by going to "Preferences" -> "Advanced" -> "Show Develop menu in menu bar"
3) Go to Develop menu -> "Simulator" or name of your computer -> select "Automatically show web inspector for JSContexts" and "Automatically pause connecting to JSContexts"
4) Re-run your project and Safari should auto-show the web inspector
Swift 5.0
The other suggestions didn't work for me, so I found a web post that explains how to do it now.Essentially
let logFunction: #convention(block) (String) -> Void = { string in
print("JS_Console:", string)
}
if let console = context.objectForKeyedSubscript("console") {
console.setObject(logFunction, forKeyedSubscript: "log") // works for me
// is this needed? "console.setObject(unsafeBitCast(logFunction, to: AnyObject.self), forKeyedSubscript: "log")
}
log.console is variadic, but I could find no way to utilize it even though the link above suggests it's possible. What I did discover though is that you can use JavaScript interpolation to get values, for example:
console.log(`getCombinedFrameYaw: ${frameYaw} rot=${pathRotation}`)

Categories