I am porting a huge angularJS app to iOS 9 and wanted to benefit from WKWebView (migrating from UIWebView). The app is locally self contained, hence all files are served for the app main bundle using file:// protocol.
Unfortunately, it sounds WKWebView originally breaks file:// protocol on iOS 8.x, but some light were casted when I saw the brand new iOS 9 loadFileURL(basePath:, allowingReadAccessToURL:) API.
let readAccessPath = NSURL(string:"app", relativeToURL:bundleURL)?.absoluteURL
webView.loadFileURL(basePath!, allowingReadAccessToURL:readAccessPath!)
Alas, while I set allowingReadAccessToURL to the root folder within my bundle (app/), I only got the "index file", no asynchronous file are loaded.
Anyone having some experience with that issue?
[UPDATE] I see my initial issue description wasn't accurate enough. I do have my HTML running. But my asynchronous angularJS calls are actually blocked by security watchdog in the WebKit framework.
A WKWebView object is a platform-native view that you use to incorporate web content seamlessly into your app's UI. A web view supports a full web-browsing experience, and presents HTML, CSS, and JavaScript content alongside your app's native views.
WKWebView. WKWebView was introduced in iOS 8 allowing app developers to implement a web browsing interface similar to that of mobile Safari. This is due, in part, to the fact that WKWebView uses the Nitro Javascript engine, the same engine used by mobile Safari.
Difference Between UIWebview and WKWebViewUIWebview is a part of UIKit, so it is available to your apps as standard. You don't need to import anything, it will we there by default. But WKWebView is run in a separate process to your app,. You need to import Webkit to use WKWebView in your app.
Using GCDWebServer is what I would recommend, you can run a local web server as http://localhost
.
Swift 3.0
Add GCDWebServer pod pod "GCDWebServer", "~> 3.0"
Click and drag your AngularJS web folder into Xcode and when prompted select 'Copy items if needed' and 'Create folder references'
Use this code in your controller to run a localhost web server
class MainViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate, GIDSignInUIDelegate {
var webView : WKWebView!
var webServer:GCDWebServer?
override func viewDidLoad() {
super.viewDidLoad()
self.webView.load(URLRequest(url: loadDefaultIndexFile()!))
}
private func loadDefaultIndexFile() -> URL? {
self.webServer = GCDWebServer()
let mainBundle = Bundle.main
// The path to my index.html is www/index.html. If using a default public folder then it could be public/index.html
let folderPath = mainBundle.path(forResource: "www", ofType: nil)
self.webServer?.addGETHandler(forBasePath: "/", directoryPath: folderPath!, indexFilename: "index.html", cacheAge: 0, allowRangeRequests: true)
do {
try self.webServer?.start(options: [
"Port": 3000,
"BindToLocalhost": true
]);
}catch{
print(error)
}
// Path should be http://localhost:3000/index.html
return self.webServer?.serverURL
}
}
Although I don't have a quick answer (I mean a quick fix) I do have a solution.
This involves giving up on file:// protocol and switch to http:// over localhost.
SHORT ANSWER
Here are the steps:
1) — Install a local Web server in your own app;
2) — Setup the local Web server to serve from localhost at a given port of your choosing;
3) — Set up the delegate that actually serve the file from your app ressources given the right mime type;
4) — Authorize to bypass iOS9 ATS to handle http (and not https only).
And voila!
DETAILED ANSWER
1) Install a local Web server in your own app;
Install the GCDWebServer fro its Github repo: https://github.com/swisspol/GCDWebServer
2) Setup the local Web server to serve from localhost at a given port of your
Given the fact your angularjs or HTML app files are located to the folder "app" in your resources folder.
In your vc ViewDidLoad:
@implementation ViewController
GCDWebServer* _webServer;
- (void)viewDidLoad
{
[super viewDidLoad];
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame];
[self.view addSubview:self.webView];
self.webView.navigationDelegate = self;
NSURL *bundleURL = [NSBundle mainBundle].bundleURL;
NSURL *basePath = nil;
// Init WebServer
[self initWebServer:[[NSURL URLWithString:@"app" relativeToURL:bundleURL] absoluteURL]];
basePath = [NSURL URLWithString:@"http://localhost:8080/page.html#/home" relativeToURL:nil];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:basePath];
[self.webView loadRequest:request];
}
3) Set up the delegate that actually serve the file from your app ressources given the right mime type;
-(void)initWebServer:(NSURL *)basePath {
// Create server
_webServer = [[GCDWebServer alloc] init];
#define GCDWebServer_DEBUG 0
#define GCDWebServer_VERBOSE 1
#define GCDWebServer_INFO 2
#define GCDWebServer_WARNING 3
#define GCDWebServer_ERROR 4
#define GCDWebServer_EXCEPTION 5
[GCDWebServer setLogLevel:GCDWebServer_ERROR];
// Add a handler to respond to GET requests on any URL
[_webServer addDefaultHandlerForMethod:@"GET"
requestClass:[GCDWebServerRequest class]
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
//NSLog([NSString stringWithFormat:@"WS: loading %@", request]);
NSString * page = request.URL.lastPathComponent;
NSString * path = request.URL.path;
NSString * file = path;
//NSLog(@"WS: loading %@", file);
NSString * fullPath = [NSString stringWithFormat:@"%@%@", basePath, path];
NSString * sFullPath = [fullPath substringFromIndex:7];
BOOL isText = NO;
if([page.lastPathComponent hasSuffix:@"html"]) {
isText = YES;
}
if (isText) {
NSError * error = nil;
NSString * html = [NSString stringWithContentsOfFile:sFullPath encoding:NSUTF8StringEncoding error: &error];
return [GCDWebServerDataResponse responseWithHTML:html];
}
else {
NSData * data = [NSData dataWithContentsOfFile:sFullPath];
if (data !=nil) {
NSString * type = @"image/jpeg";
if ([page.lastPathComponent hasSuffix:@"jpg"]) type = @"image/jpeg";
else if ([page.lastPathComponent hasSuffix:@"png"]) type = @"image/png";
else if ([page.lastPathComponent hasSuffix:@"css"]) type = @"text/css";
else if ([page.lastPathComponent hasSuffix:@"js" ]) type = @"text/javascript";
return [GCDWebServerDataResponse responseWithData:data contentType:type];
}
else {
return [GCDWebServerDataResponse responseWithHTML:[NSString stringWithFormat:@"<html><body><p>404 : unknown file %@ World</p></body></html>", sFullPath]];
//return [GCDWebServerDataResponse responseWithHTML:@"<html><body><p>Hello World</p></body></html>"];
}
}
}];
// Start server on port 8080
[_webServer startWithPort:8080 bonjourName:nil];
NSLog(@"Visiting %@", _webServer.serverURL);
}
4) Authorize to bypass iOS9 ATS to handle http (and not https only)
In your info.plist file in Xcode, you must add a dictionary named "App Transport Security Settings" with inside a key-value as follows:
NSAllowsArbitraryLoads = true
Hope it helps. Anyone who stumble upon something simpler is welcome to answer!
As I can say, you are making mobile application and compiling it after... I had quite similar issue. But for me final build was made by adobe build.phonegap service. And there the only thing I had to do is to add cordova-whitelist-plugin in config.xml like this
< gap:plugin name="cordova-plugin-whitelist" source="npm" version="1.0.0" />
And than add permission for accessing such links
<allow-intent href="file:///*/*" />
also into config.xml
Sorry, if I understood you wrong.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With