There is a controller with a WKWebView "Parent" that opens (pushes) another controller with another WKWebView "Child".
When Child modifies the localStorage, sessionStorage or IndexedDB then pops back to Parent, Parent is NOT aware of the changes and needs to be reloaded to see the new values set by Child.
It means that both WKWebView share the same localStorage, sessionStorage, indexedDB, but they need a refresh of the content to see the modifications.
How to share in real-time the same storage between WKWebView?
The init of WKWebViews can receive a parameter WKWebViewConfiguration with a variable called processPool that represents all the web content (including localStorage, IndexedDB, cookies and so on) process.
So for the WKWebViews to share in real-time all the memory management, you need to declare a unique WKProcessPool for all of them.
let uniqueProcessPool = WKProcessPool()
let configA = WKWebViewConfiguration()
configA.processPool = uniqueProcessPool
let webViewA = init(frame: CGRect.zero, configuration: configA)
let configB = WKWebViewConfiguration()
configB.processPool = uniqueProcessPool
let webViewB = init(frame: CGRect.zero, configuration: configB)
Related
EDIT for sake of brevity
Background: I have a fully operational Game using html, css, js.
Goal: convert that to a WKWebView iOS App using Xcode.
Why bother? = to learn!
swift -> .js
How do I communicate connection of my Gamepad pointed towards the Simulator to my .js code embedded in my WKWebView?
.js -> swift
And later, how do I communicate gamepiece movement in the .js code to the Simulator?
END EDIT
IF THIS IS AMPLE, STOP HERE
The embedded connection code in the .js is:
window.addEventListener("gamepadconnected", (event) => {..}
and
window.addEventListener("gamepaddisconnected", (event) => {..}
The movement code embedded in the .js are various HTML5 calls.
The details are:
Within my GameViewController.swift, I have:
var theWebView: WKWebView!
class GameViewController: UIViewController, WKNavigationDelegate {..}
.. and within the latter, I have:
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
theWebView = WKWebView(frame: .zero, configuration: webConfiguration)
theWebView.navigationDelegate = self
view = loadURL(webAddress: "https://www.lovesongforever.com/firstgame")
} // loadView
and
func loadURL(webAddress: String) -> WKWebView {
let theURL = URL(string: webAddress)
let theRequest = URLRequest(url: theURL!)
theWebView.load(theRequest)
theWebView.allowsBackForwardNavigationGestures = false
return theWebView
} // loadURL
Up until now, I believed once a WKWebView was instantiated with my https web site as shown above, that the web site code took control, leaving Xcode to display the result within the GameScene
All my html, css, js code segments play nicely together and successfully generate the initial Splash Screen, followed by the resulting Game Scene.
my initial Game Scene
At this point, my Game Controller is on, but the above
window.addEventListener(..)
routines are not accessed and so my Game Controller is rendered mute. All Game Pieces do not move.
FWIW, if the addEventListener routine were accessed, the Stop Sign would be a Check Mark.
Am I missing stuff in GameScene.swift?
If I were building my Game from scratch, using only Xcode, GameScene.swift would hold NotificationCenter calls, together with the various funcs that would move the game pieces in response to button presses on Gamepad.
But I am not starting from scratch, but rather from a operational web site.
So, what do I need to change to make that conversion successful?
I have a NSViewController which displays various WKWebView at specific locations inside a NSView. If the frame of the WKWebView gets small - it switches to the mobile version of the website. Is there a way to always show the full (desktop version) website in the WKWebView?
My first idea was to inject some javascript and set the viewport to a specific size to force the website to display a "Desktop" view.
let viewportScriptString = "var meta=document.createElement('meta');meta.name=\"viewport\";meta.content=\"width=1920\";document.getElementsByTagName('head')[0].appendChild(meta);"
let viewportScript = WKUserScript(source: viewportScriptString, injectionTime: .AtDocumentStart, forMainFrameOnly: true)
let controller = WKUserContentController()
controller.addUserScript(viewportScript)
let config = WKWebViewConfiguration()
config.userContentController = controller
let nativeWebView = WKWebView(frame: CGRect.zero, configuration: config)
Unfortunately this does not work and the website is still scaled. To demonstrate the behaviour i try to achieve please see the following screenshot.
==EDIT==
thanks for pointing me to some similar questions. I tried the following the solutions that worked for other users. Unfortunately I had no luck. Maybe WKWebView is different on macOS - the other questions are about iOS.
NSUserDefaults
setting the CustomUserAgent on WKWebView
setting ApplicationNameForUserAgent on WKWebViewConfiguration
adding a User-Agent to NSUrlRequest
You should change the user agent, for example:
UserDefaults.standard.register(defaults: ["UserAgent" : "Chrome Safari"])
I'm developing a browser app. I'd like to restore WKWebView object when app is restarted. So I serialized WKWebView object and saved to Realm. But When I deserialized WKWebView object, the data of url and backList was losted from WKWebView.
let WebView = WKWebView()
WebView.frame = CGRect(x: 0, y: 0, width:300, height:650)
let urlString = "http://www.google.com"
let encodedUrlString = urlString.addingPercentEncoding(withAllowedCharacters:NSCharacterSet.urlQueryAllowed)
let url = NSURL(string: encodedUrlString!)
let request = NSURLRequest(url: url! as URL)
WebView.load(request as URLRequest)
let serialized_WebView = NSKeyedArchiver.archivedData(withRootObject: WebView) as NSData //convert to NSData
let deserialized_WebView = NSKeyedUnarchiver.unarchiveObject(with: serialized_WebView as! Data) as! WKWebView
print("frame",deserialized_WebView.frame) // frame (0.0, 0.0, 300.0, 650.0)
print("url",deserialized_WebView.url) // url nil
print("backList",deserialized_WebView.backForwardList.backList) // backList []
I'd like to restore not only url but backList as a part of WKWebView object. I do not want to do implement unique function of save backList if possible.
Any advice please.
I've honestly never seen anyone encode a view like this before. Generally speaking, views' conformance to NSCoding is intended for use by Interface Builder and any properties that can be set will probably be exposed there.
Looking in Interface Builder, the serialized properties are navigation gestures, user agent, link preview and a bunch of properties of WKWebViewConfiguration. I've confirmed in a breakpoint that this is all it encode.
The backList represents the state of the view at a given time and were it encoded it would be in WKWebView.encodeRestorableState(with:) for use in UIKit's state restoration process. Unfortunately, this method is not implemented and no state is saved. In this answer, matt recommends using UIWebView if you want to reconstruct history in a web view.
Another, more complex approach is what FireFox for iOS does. They do successfully restore history in a WKWebView using a trick with a custom web request server and a web page that injects URLs into the history via JavaScript's history.pushState. The relevant bits of code are:
Tab.restore(_:WKWebView): Their code which takes an array of URLs and hands things off to their local web server.
SessionRestoreHandler: The request handler for their local web server which returns the web page.
SessionRestore.html: The file served by the request handler, which injects the history with JavaScript.
This approach may work for one of the major players in the browser space, but it's probably not worth the trouble for most apps. I personally would recommend just sticking with a UIWebView and state restoration like matt suggested.
I have a url document url list in a TableViewController` like this one
https://lists.w3.org/Archives/Public/uri/2004Nov/att-0015/App-Note-UseOfTheFileURLInJDF-031111.doc
and on TableViewCell selection this doc file should open in any default viewer that is not part my app, So I can do achieve this ? Is this possible or any suggestion.
Working with QuickLook Framework should satisfied your requirement.
As mentioned at "Using the Quick Look Framework":
A Quick Look preview controller can display previews for the following
items:
iWork documents
Microsoft Office documents (Office ‘97 and newer)
Rich Text Format (RTF) documents
PDF files
Images
Text files whose uniform type identifier (UTI) conforms to the public.text type
Comma-separated value (csv) files
You can find many of articles about working with QuickLook Framework; You might want to check the following one:
Using Quick Look Framework for Previewing Documents.
Also, checking this repo (GitHub) might be useful.
Hope this helped.
Use UIWebView class to open the given URL.
Method 1:
let webView = UIWebView(frame: self.view.frame)
webView.scalesPageToFit = true
view.addSubview(webView)
let urlS = "https://lists.w3.org/Archives/Public/uri/2004Nov/att-0015/App-Note-UseOfTheFileURLInJDF-031111.doc"
let url = URL(string: urlS)
let request = URLRequest(url: url!)
webView.loadRequest(request)
Method 2:
With this method, you'll get nice ToolBar items, which you can customize based on your requirement.
Using UIWebView library for Swift, SwiftWebView:
If you're using UINavigationController:
let urlS = "https://lists.w3.org/Archives/Public/uri/2004Nov/att-0015/App-Note-UseOfTheFileURLInJDF-031111.doc"
let webVC = SwiftWebVC(urlString: urlS)
self.navigationController?.pushViewController(webVC, animated: true)
OR
If you want to present Modally:
let urlS = "https://lists.w3.org/Archives/Public/uri/2004Nov/att-0015/App-Note-UseOfTheFileURLInJDF-031111.doc"
let webVC = SwiftModalWebVC(urlString: urlS)
self.present(webVC, animated: true, completion: nil)
I want to load local resources with webView. I built a demo with both UIWebView and WKWebView to do some test with the code below.
let uiWebView = UIWebView(frame: self.view.bounds)
self.view.addSubview(uiWebView)
let wkWebView = WKWebView(frame:CGRect(x: 0, y: 400, width: 500, height: 500))
self.view.addSubview(wkWebView)
let path = Bundle.main.path(forResource:"1", ofType: "png")
guard let realPath = path else {
return
}
let url = URL(string: realPath)
let fileUrl = URL(fileURLWithPath: realPath)
if let realUrl = url {
uiWebView.loadRequest(URLRequest(url:realUrl))
wkWebView.load(URLRequest(url:realUrl))
}
// uiWebView.loadRequest(URLRequest(url:fileUrl))
// wkWebView.load(URLRequest(url:fileUrl))
The uiWebView can load the resource but wkWebView can not. But if I use
uiWebView.loadRequest(URLRequest(url:fileUrl))
wkWebView.load(URLRequest(url:fileUrl))
both uiWebView and wkWebView can work well.
I am confused and can anyone explain that for me:
Shouldn't I use URL(string: realPath) for a local resource? But why UIWebView can use it ?
A couple points:
Apple recommends that you use WKWebview for iOS 8 and later. I would avoid writing new code with UIWebView.
In apps that run in iOS 8 and later, use the WKWebView class instead of using UIWebView. Additionally, consider setting the WKPreferences property javaScriptEnabled to false if you render files that are not supposed to run JavaScript.
Apple has been trying to move away from path and instead wants to use URI even for local files. They recommend that you NOT use /path/to/file.png and use file:///path/to/file.png instead.
As to why one URL works and the other does not, let's make a minimal example:
let realPath = "/path/to/file.png"
let url = URL(string: realPath) // /path/to/file.png
let fileUrl = URL(fileURLWithPath: realPath) // file:///path/to/file.png
url does not provide the scheme (a.k.a protocol). It should only be used in conjunction with another URL to give the absolute address of the resource you are trying to reach. UIWebView supports it for backwards-compatibility reasons but Apple decided to start clean with WKWebView.
fileURL has a scheme (file://) that tells the resource is located on the local file system. Other common schemes are http, https, ftp, etc. It's a complete address to a resource so both views know how to resolve it.
This might be for security reasons, or just how the WKWebView API was implemented.
WKWebView has a specific instance method for loading local resources called loadFileURL(_:allowingReadAccessTo:). This was introduced in iOS 9.
Note
If you are targeting iOS 8.0 or newer, you should be using WKWebView instead of UIWebView. See: https://developer.apple.com/reference/webkit/wkwebview