User permission from webViewKit button click - ios

Using swift 4 and IOS 12 ,i am using a webViewKit to load a website. One of the features of the web app is to scan qrcode, i have successfully loaded the website but i am still struggling how to access the device camera to scan qr code, in android everything works fine including this qr code scanner using webView onPermissionRequest method. I have already tried this
Key : Privacy - Camera Usage Description in info.plist
and also adding these lines of codes here AVCaptureDevice.authorizationStatus(for: .video) on url load before pressing the web button to scan qr code. According to our web developer this button press executes a javascript undefined function, but i don't know how to make webViewKit respond to this button click event and the hardest part is that there are no logs or NSLogs to evaluate. I am new to IOS development for about almost a month. Thank you
var WebCodeCamJS = function(element) {
'use strict';
this.Version = {
name: 'WebCodeCamJS',
version: '2.7.0',
author: 'Tóth András',
};
var mediaDevices = window.navigator.mediaDevices;
mediaDevices.getUserMedia = function(c) {
return new Promise(function(y, n) {
(window.navigator.getUserMedia || window.navigator.mozGetUserMedia || window.navigator.webkitGetUserMedia).call(navigator, c, y, n);
});
}
HTMLVideoElement.prototype.streamSrc = ('srcObject' in HTMLVideoElement.prototype) ? function(stream) {
this.srcObject = !!stream ? stream : null;
} : function(stream) {
if (!!stream) {
this.src = (window.URL || window.webkitURL).createObjectURL(stream);
} else {
this.removeAttribute('src');
}
};
Above code is a js function named webcodecamjs.js that is being executed from a button click event on the web page. I found similar problem with solution here , but i'm not quite sure how to implement it, it says
Now Add JS file (WebRTC.js) that defines various WebRTC classes, functions & passes the calls to the WKWebView.
In the WKWebView inject the script at the document start:
where do i put this WebRTC.js file? what i did was to create a new file in my ios project and named it WebRTC.js , i also tried renaming it to webcodecamjs.js like what we have in web js files, how i did it in func viewDidAppear
let contentController = WKUserContentController()
contentController.add(self, name: "callbackHandler")
let script = try! String(contentsOf: Bundle.main.url(forResource: "webcodecamjs", withExtension: "js")!, encoding: String.Encoding.utf8)
contentController.addUserScript(WKUserScript(source: script, injectionTime: WKUserScriptInjectionTime.atDocumentStart, forMainFrameOnly: true))
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
let configuration = WKWebViewConfiguration()
configuration.preferences = preferences
configuration.userContentController = contentController
configuration.allowsPictureInPictureMediaPlayback = true
webView.navigationDelegate = self
webView.configuration.preferences = configuration.preferences
WKWebView.load(webView)(NSURLRequest(url: NSURL(string: encodedLoginUrl)! as URL) as URLRequest)
webView is a
#IBOutlet weak var webView: WKWebView
not declared as below
var webView: WKWebView
that's why i can't do something like below, cause it gives me 'weak' warning
webView = WKWebView(frame: CGRect.zero, configuration: config)

Could you add code snippet to check?

Related

how to pass values to a web view which is loading a react native app

i am new to react native and very familiar to iOS with swift language.My requirement is in an iOS existing app there will be a web view which is loading a react native app ,here i want to use some values of iOS app in react native.
for example , in iOS app there will be an authorization code ,this auth code i want to use in react native app which is loading in UIWebview.
override func viewDidLoad() {
super.viewDidLoad()
loadReactNativeApp()
}
func loadReactNativeApp(){
let link = "https://staging.abcdefg.com:1442/appname_web/"
let finalUrl:URL!
finalUrl = URL(string: link)
let urlRequest = URLRequest.init(url: finalUrl!)
webview.load(urlRequest)
}
I don't have idea how could we pass the values to react native app.Can any one help me to solve my problem would be great.Thankyou
You have to use the injectedJavaScript prop from WebView.
Eg:
First declare a jsCode variable:
const amount = 2
const jscode = `
if (document.getElementById('txtAmount') == null) {
// field not existing, deal with the error
} else {
document.getElementById('txtAmount').value = '${amount}';
}
`
Please notice the " ` " character. Used to put variables in strings.
Then use it like so:
<WebViewBridge
injectedJavaScript={jsCode}

Preload multiple local WebViews

Because I could just find some outdated information / not working solutions to my problem I decided to ask this kind of question again. (See outdated / wrong solutions:)
WKWebView: Is it possible to preload multiple URLs?
(Xcode, Swift) Is it possible to load multiple WKWebViews simultaneously if they are on different viewControllers?
Swift 3, iOS 10.3 - Preload UIWebView during Launch Screen
My app is separated in one native part and one HTML part. The HTML is saved as a local file (index.html) and should be load into the myWebView view.
#IBOutlet weak var myWebView: UIWebView!
func loadWebview() {
let url = Bundle.main.url(forResource: "index", withExtension: "html")
let request = URLRequest(url: url!)
myWebView.loadRequest(request)
myWebView.scrollView.isScrollEnabled = false
myWebView.allowsLinkPreview = false
myWebView.delegate = self
}
Because my DOM tree is very large, switching from the native part to the web part (on button click) takes quite a long time at -least for the first time switching- because afterward, I'm sure the webView-request gets cached.
To my question: How can I preload the WebView on app init to avoid the white screen (maybe 0.5s - 1s duration) when switching from the native to the Web part?
EDIT:
The WKWebView is displaying the scrollbar while the UIWebView was not!
Using (like with UIWebView) this styles:
::-webkit-scrollbar {
display: none;
}
is not working and adding these lines:
webview.scrollView.showsHorizontalScrollIndicator = false
webview.scrollView.showsVerticalScrollIndicator = false
is also not working at all.
Firstly, you should switch to WKWebView,UIWebView is no longer recommended to be used by Apple.
Secondly, you can create a pool of web views that get created and asked to load when the app starts. This way by the time the user switches to the web interface the web view might've got a chance to fully load.
For this you can use a class like this:
/// Keeps a cache of webviews and starts loading them the first time they are queried
class WebViewPreloader {
var webviews = [URL: WKWebView]()
/// Registers a web view for preloading. If an webview for that URL already
/// exists, the web view reloads the request
///
/// - Parameter url: the URL to preload
func preload(url: URL) {
webview(for: url).load(URLRequest(url: url))
}
/// Creates or returns an already cached webview for the given URL.
/// If the webview doesn't exist, it gets created and asked to load the URL
///
/// - Parameter url: the URL to prefecth
/// - Returns: a new or existing web view
func webview(for url: URL) -> WKWebView {
if let cachedWebView = webviews[url] { return cachedWebView }
let webview = WKWebView(frame: .zero)
webview.load(URLRequest(url: url))
webviews[url] = webview
return webview
}
}
and ask it to preload the url sometimes during the app startup:
// extension added for convenience, as we'll use the index url in at least
// two places
extension Bundle {
var indexURL: URL { return self.url(forResource: "index", withExtension: "html")! }
}
webviewPreloader.preload(url: Bundle.main.indexURL)
Thirdly, you might need to use a container view instead of the actual web view, in your controller:
#IBOutlet weak var webviewContainer: UIView!
What remains is to add the preloaded web view to the container when needed:
func loadWebview() {
// retrieve the preloaded web view, add it to the container
let webview = webviewPreloader.webview(for: Bundle.main.indexURL)
webview.frame = webviewContainer.bounds
webview.translatesAutoresizingMaskIntoConstraints = true
webview.autoresizingMask = [.flexibleWidth, .flexibleHeight]
webviewContainer.addSubview(webview)
}
And not lastly, be aware that keeping alive instances of web views, might carry performance penalties - memory and CPU-wise.

How do I prevent a WKWebView from presenting the Camera modal if a user has denied access to the camera?

In my app, I have a WKWebView loading a website with file input that allows a user to upload images via the Camera or the Photo Library.
My app has both privacy usage descriptions for the Camera and Photo Library.
If a user has denied access to the Camera, the WKWebView will still show the option to upload images via the Camera, and the Camera modal presents (though it only shows black where you'd normally see an image of what the camera is showing). If a user swipes to the Video option in the Camera modal, the app crashes.
The relevant stack trace shows:
3 TCC __TCCAccessRequest_block_invoke_2.80 + 222
4 TCC __CRASHING_DUE_TO_PRIVACY_VIOLATION__ + 682
Is it possible from the native app side (suppose I can't edit the HTML loaded) to prevent the WKWebView from presenting the Camera modal in this case?
Because you mentioned __CRASHING_DUE_TO_PRIVACY_VIOLATION__ the app is probably actually crashing because you haven't added the iOS10 permission description for using the microphone. The camera view will trigger this additional microphone permissions popup if you choose Video. In iOS10, you must fill in the description that is presented to the user or the app will crash as you have reported. In iOS9, it just presents the generic permissions popup.
Try adding a description for the key NSMicrophoneUsageDescription in your Info.plist file. In Xcode this is called Privacy - Microphone Usage Description
This other answer will give you more details of the various privacy keys and their descriptions.
It'd appear to be a bug since it only crashes when Video is selected. I found a workaround by injecting JS:
Set the content type to only allow photos. (sample code included below)
Delete/Hide the input-file
Disable the input-file
func requestCamera() {
AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo) { response in
if response {
self.injectJavascript()
} else {
self.webView = WKWebView(frame: .zero)
}
self.setupRequest()
}
}
func injectJavascript() {
let webConfiguration = WKWebViewConfiguration()
let contentController = WKUserContentController()
let js = "var fileInput = document.getElementById('allMedia'); fileInput.setAttribute('accept', 'image/*');"
let userScript = WKUserScript(source: js, injectionTime: WKUserScriptInjectionTime.atDocumentEnd, forMainFrameOnly: false)
contentController.addUserScript(userScript)
webConfiguration.userContentController = contentController
webView = WKWebView(frame: .zero, configuration: webConfiguration)
}
func setupRequest() {
let url = URL(string: "...")!
let request = URLRequest(url: url)
webView.load(request)
}
Flow is as follows:
Camera access allowed ?
Yes
Load WKWebView normally
No
Inject JS
Setup WKWebView
Load WKWebView's request

swift 3 - How to close a page (tab) inside a webview

In a viewcontroller I am using this code to open a local html file
self.webView = WKWebView(frame: webFrame, configuration: config)
self.webView?.uiDelegate = self as? WKUIDelegate
self.webView?.navigationDelegate = self
self.webView?.scrollView.bounces = false
self.container!.addSubview(webView!)
let testUrl: URL! = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "assets")
self.webView!.loadFileURL(testUrl, allowingReadAccessTo: testUrl)
Debugging by safari in the menu Developer> Simulator appears the open page (index.html) It happens that every time I leave this viewControler and return is added in the menu Developer> Simulator a new page (index.html, index.html, ...) . I believe that new pages are opened in new tabs
In the function I use to exit this viewController I use this code
self.webView?.removeFromSuperview()
self.container.removeFromSuperview()
The objects are removed but the pages are not, does anyone know how to remove them?

How to hide the keyboard assistant bar

I'm working on an iPad app, and I am unable to hide the UIKeyboardAssistantBar, the bar shown above the soft keyboard, with text prediction etc. See picture below, which shows the full keyboard, just to give a reference - the bar I want to hide is above the keyboard (the one displaying "2")
The problem I have is when an external keyboard is used: the soft keyboard is not shown when a text view obtains the focus, but that assistant bar is always shown instead - the only way I've found so far is to let the user manually hide it, using the icon at the far right.
Ideally, the solution I'm looking for is a global call that enables or disables that, so that I don't have to handle that individually for each text view.
Any idea?
There is a trick that you can try. Here is the code:
let item = self.yourTextView.inputAssistantItem;
item.leadingBarButtonGroups = [];
item.trailingBarButtonGroups = [];
The accepted solution will hide the leading and trailing BarButtonGroups on the keyboard, however unfortunately it will not hide the suggestion/auto correct bar (the center buttons with the suggested "2".
My need was for an iPad native app that required an HTML login page using a WKWebView to render the HTML login page. To hide the suggestion buttons, I used some injected javascript because I had no control over the HTML login page. Swift 3 code below creates the WKWebView (replaces the view object and injects the userScript into the page):
var webView: WKWebView!
override func loadView() {
let autocorrectJavaScript = "var inputTextElement = document.getElementById('userName');"
+ " if (inputTextElement != null) {"
+ " var autocorrectAttribute = document.createAttribute('autocorrect');"
+ " autocorrectAttribute.value = 'off';"
+ " inputTextElement.setAttributeNode(autocorrectAttribute);"
+ " }"
let userScript = WKUserScript(source: autocorrectJavaScript, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
let webConfiguration = WKWebViewConfiguration()
webConfiguration.userContentController.addUserScript(userScript)
webView = WKWebView(frame: .zero, configuration: webConfiguration)
view = webView
}
Actually, here is a method that works, even with contenteditable enabled:
func findKeyboardAssistantView() -> UIView? {
let result: UIView? = nil
let windows = UIApplication.shared.windows
let prefixes = [
"<UIInputSetContainerView",
"<UIInputSetHostView",
"<_UIKBCompatInputView",
"<UIKeyboardAutomatic",
"<UIKeyboardImpl",
]
for window in windows {
if window.description.hasPrefix("<UIRemoteKeyboardWindow") {
var last = window.subviews
for p in prefixes {
for s in last {
if s.description.hasPrefix(p) {
last = s.subviews
}
}
}
for s in last {
if s.description.hasPrefix("<UIKeyboardAssistantBar") {
return s
}
}
break
}
}
return result
}
findKeyboardAssistantView()?.isHidden = true
Note that it has to be fired when UIResponder.keyboardWillShowNotification is sent

Resources