How to load WebWorker from local in WKWebView - wkwebview

I rebuild an iOS app from a Vue.js app. It uses a WebWorker to execute a script in the background.
I confirmed that npm run serve on the local could run the app.
So the following process is to run the app in a WKWebView.
The app loads the files from a local device. The files such as index.html and main.js could be loaded and shown correctly.
let url = Bundle.main.url(forResource: "index", withExtension: "html")!
webView.loadFileURL(url, allowingReadAccessTo: url)
let request = URLRequest(url: url)
webView.load(request)
However, the worker seems not to be loaded. The worker script is located in the same directory to index.html.
// in my script
this.worker = new Worker('worker.js');
I also tried running a quite simple HTTP Web server to load worker.js.
guard let listener = try? NWListener(using: .tcp, on: NWEndpoint.Port(integerLiteral: 52525)) else {
print("### Error.")
self.listener = nil
return
}
listener.newConnectionHandler = { connection in
connection.start(queue: .main)
connection.stateUpdateHandler = { state in
switch state {
case .ready:
connection.send(content:
("""
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Length: 86810
Content-Type: application/javascript
""".replacingOccurrences(of: "\n", with: "\r\n") + worker).data(using: .utf8), completion: .idempotent)
default:
break
}
}
}
listener.start(queue: .main)
self.listener = listener
Safari in iOS Simulator could get worker.js and show its content.
// the app can send a response by HTTP.
this.worker = new Worker('http://localhost:52525/worker.js');
However, the error message said SecurityError: The operation is insecure. How can I use a WebWorker in WKWebView? Is any configuration needed?

According to MDN page, a script tag with type="text/js-worker" can be used for the alternative of worker.js file.
<script id="worker" type="text/js-worker">
<!-- the contents of worker.js -->
</script>
const worker = new Worker(window.URL.createObjectURL(
new Blob([document.getElementById("worker").textContent], {
type: "application/javascript",
})
));
I tried this approach, and then the worker could run.

Related

Angular +Workbox = build ChunkLoadError: Loading chunk # and Refused to execute script because its MIME

I have added Workbox to Angular in first production deploy everything works fine, but after updating a module and rebuilding angular and injecting Workbox then visiting the site i see the service worker updates to the new version and refreshes the page, but now trying to use the updated module I get errors
Refused to execute script from 'https://example.com/8-es2015.203674bf0547eff7ff27.js'
because its MIME type ('text/html') is not executable,
and strict MIME type checking is enabled.
main-es2015.45ba4a512f87eefb1b3a.js:1 ERROR Error: Uncaught (in promise): ChunkLoadError:
Loading chunk 8 failed.(error: https://example.com/8-es2015.203674bf0547eff7ff27.js)
ChunkLoadError: Loading chunk 8 failed......
I looked at the network in chrome and I see that the file 8-es2015.203674bf0547eff7ff27.js is being served from the (disk cache) unlike the rest of the files which get served by (ServiceWorker), its content is the index.html file I don't know where it came from its not even part of the new build ? chrome places it in top frame section under scripts
Whats the reason for this Error, in the angular.json I have "outputHashing": "all", I delete everything and rebuild but still this errors, its until I clear the browser cash remove the ServiceWorker and hard refresh that the error stops happening until I reload page and it returns. Do I need to delete all the cache after every update, I thought Workbox does this automatically.Do I add something like so in the sw.js
self.addEventListener('activate', event => event.waitUntil(
caches.keys().then(cacheNames => cacheNames.forEach(name => caches.delete(name)))
)
);
Am using express, so I have set the maxAge on the sw.js to 0 and even change the public route to static files to a deep route but nothing
app.use('/sw.js', express.static(path.resolve('./public/dist/static/sw.js'), {maxAge: 0}));
app.use('/', express.static(path.resolve('./public/dist/static/'), {maxAge: 86400000}));
tools: angular 8.2.4 - workbox 4.3.1
Update
Removed workbox and the app worked, am guessing its cause of their new package workbox-window or the way am trying to use it. I have placed it in module service that is loaded from app.module then the service is called from a AppComponent ngOnInit. This could be the wrong way of initializing it.
code setup:
import {Workbox} from 'workbox-window';
#Injectable()
export class WorkerService {
supportWorker: boolean;
supportPush: boolean;
constructor(#Inject(WINDOW) private window: any, private loggerService: LoggerService) {
this.supportWorker = ('serviceWorker' in navigator);
this.supportPush = (this.supportWorker && 'PushManager' in window);
}
initWorker() {
if (this.supportWorker && environment.production) {
const wb = new Workbox('sw.js');
if (wb) {
wb.addEventListener('installed', event => {
if (event.isUpdate) {
// output a toast translated message to users
this.loggerService.info('App.webWorkerUpdate', 10000);
setTimeout(() => this.window.location.reload(), 10000);
}
});
wb.addEventListener('activated', event => {
if (!event.isUpdate) {
this.loggerService.success('App.webWorkerInit', 10000);
}
});
wb.register();
}
}
}
}
This the app component, i thought it would be best to add it to main.ts after bootstrapModule.then() but I don't know how inject a service in this method
#Component({
selector: 'app-root',
template: '<route-handler></route-handler>'
})
export class AppComponent implements OnInit {
constructor(private ws: WorkerService) {
}
ngOnInit(): void {
this.ws.initWorker();
}
}
After setting up Workbox in a different way it worked, the problem effected even chrome which failed to clear all cache after each build when testing it, had to use incognito to make sure everything works.
Here is the solution thanks to Ralph Schaer article a must read. His method is not to Cache-Bust the chunks angular build generates, also he globs in all the production scripts of workbox used by the app into the build folder and finally in the index.html he calls on workbox-window to register the service-worker.

Using react-hot-loader 3 with own server

I was able to set up react-hot-loader to work properly bundling my client js and pushing changes to browser and applying there (except [react-router] You cannot change <Router routes>; it will be ignored warning).
I'm using my own server with koa, koa-webpack-dev-middleware and koa-webpack-hot-middleware, that processes webpack and hot. It also handles server rendering of my app with this code
export default function *renderReact() {
const history = createMemoryHistory();
const store = makeStore(history);
const [redirectLocation, renderProps] = yield match.bind(null, { routes, location: this.url, history });
if (redirectLocation) {
return this.redirect(redirectLocation.pathname + redirectLocation.search)
}
if (renderProps == null) {
return this.throw(404, 'Not found')
}
const rootElement = (
<Provider store={store} key="provider">
<RouterContext {...renderProps} />
</Provider>
);
this.body = renderApp({
html: yield store.renderToString(ReactDOMServer, rootElement),
state: JSON.stringify(store.getState())
});
}
The problem is with my server side code: hot works only for client code and updates changes on the fly, but my server code does not get updated on changes as scripts a loaded on server start and on page reload i get not updated rendered page from server and then it updates with new client code.
and react warning Warning: React attempted to reuse markup in a container but the checksum was invalid...
The question is: how to handle code changes related to server rendering part on server not restarting the app as it breaks hot loading?

Flash policy-file-request

I am running nodejs based server and I want to use Flash as the interface.
in AS3 I write:
Security.loadPolicyFile("xmlsocket://151.248.124.213:3843");
so that should load policy file at this adress http://151.248.124.213:3843/.
links wouldn't work for now. but here is content of policy file:
<cross-domain-policy>
<allow-access-from domain="*" to-ports="*"/>
<allow-access-from domain="151.248.124.213" to-ports="80"/>
</cross-domain-policy>
And here is the application http://151.248.124.213/1.html
But when I start using it, it sends me the message:
<policy-file-request/>
AS3 talking to server at the same IP:3000.
Nodejs is on VDS server and runs perfectly. When I start SWF from Flash Builder, everything works fine. So the problem must be somewhere in the policy file or in AS3 trying to load one.
I solved the problem with this code in policy file server
var net = require('net');
var netserver = net.createServer(function(socket){
socket.addListener("error",function(err){
socket.end && socket.end() || socket.destroy && socket.destroy();
});
var xml = '<?xml version="1.0"?>\n<!DOCTYPE cross-domain-policy SYSTEM \n"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">\n<cross-domain-policy>\n';
xml += '<site-control permitted-cross-domain-policies="master-only"/>\n';
xml += '<allow-access-from domain="*" to-ports="*"/>\n';
xml += '</cross-domain-policy>\n';
if(socket && socket.readyState == 'open'){
socket.write(xml);
socket.end();
}
});
netserver.addListener("error",function(err){});
netserver.listen(3843, '0.0.0.0');
'0.0.0.0' - that is your IP or domain
And that is what you use to connect from flash
Security.loadPolicyFile("xmlsocket://151.248.124.213:3843");
if you want me to explain some details, let me know.

one node process for 2 or more different web page

I am starting using nodejs. To date I can open a web page index.html using an app.js node application.
Example from http://blog.kevinchisholm.com/javascript/node-js/making-a-simple-http-server-with-node-js-part-ii/ :
//step 1) require the modules we need
var
http = require('http'),//helps with http methods
path = require('path'),//helps with file paths
fs = require('fs');//helps with file system tasks
//a helper function to handle HTTP requests
function requestHandler(req, res) {
var
content = '',
fileName = path.basename(req.url),//the file that was requested
localFolder = __dirname + '/public/';//where our public files are located
//NOTE: __dirname returns the root folder that
//this javascript file is in.
if(fileName === 'index.html'){//if index.html was requested...
content = localFolder + fileName;//setup the file name to be returned
//reads the file referenced by 'content'
//and then calls the anonymous function we pass in
fs.readFile(content,function(err,contents){
//if the fileRead was successful...
if(!err){
//send the contents of index.html
//and then close the request
res.end(contents);
} else {
//otherwise, let us inspect the eror
//in the console
console.dir(err);
};
});
} else {
//if the file was not found, set a 404 header...
res.writeHead(404, {'Content-Type': 'text/html'});
//send a custom 'file not found' message
//and then close the request
res.end('<h1>Sorry, the page you are looking for cannot be found.</h1>');
};
};
//step 2) create the server
http.createServer(requestHandler)
//step 3) listen for an HTTP request on port 3000
.listen(3000);
But I don't know if it is possible to open different web pages that will use the same app.js. It has to be possible.But how to modify the code above?
Problem:
how to modify the code above to use app.js able to open index.html and other page web index2.html?. The content of index.html is different from index2.html but both use the same app.js
You will need a routing system to route different requests to different handlers. There are some node modules that provide you this functionality. Some of them are: Connect, Express.js, and Restify.js

Node.js run as a service not working. it just starts then stops

Trying to a run Node.js as a Windows service. I have used nssm.exe to create the service, and it starts then immediately stops.
The error - "Could not resume ListenerNodeJS Service on Local Computer. The service did not return an error. This could be an internal Windows error or an internal service error. If the problem persists, contact your system administrator."
I have also tried using srvstart and that did the same thing, but this time the error was "the service has started and stop. Some Services stop automatically if they have no work to do" (etc...)
Here is the contents of listener.js (the javascript I wish to run as a service)
///////////////////////////////////////////////
// get the querystring
//////////////////////////////////////////////
var http = require("http"), querystring = require("querystring");
var dirName="";
var pathName="v:/Opportunities/";
http.createServer(function(req, res) {
//parse everything after the "?" into key/value pairs
var qs = querystring.parse(req.url.split("?")[1]),
dirName =pathName + qs.dirName;
html = "" + "Directory to write " + dirName;
res.end(html);
createDir(dirName);
}).listen(1337, '0.0.0.0');
///////////////////////////////////////////////
// function: createDir Creates a directory from the querystring
///////////////////////////////////////////////
function createDir(d){
var fs = require('fs');
//see if the directory exists
if (!(fs.existsSync(d))) {
console.log("Directory does not exists. Creating directory...");
fs.mkdir(d);
///create sub folders
fs.mkdir(d+"/Drawings");
fs.mkdir(d+"/Pictures");
fs.mkdir(d+"/PO_Contracts");
fs.mkdir(d+"/Proposals");
fs.mkdir(d+"/Quotes");
fs.mkdir(d+"/SOW");
fs.mkdir(d+"/Vendor_Quotes");
}
else {
console.log("Directory exists.");
}
}
I had to not use Program Files as the location of nodejs.exe. I simply copied nodejs directory over to another location (C:\nodejs) To solve the file diretory writing issue, I had to use a real disk letter, not a mapped drive. Mapping it wouldn't make sense to a service, so therefore I use the real drive letter.

Resources