Display web content in windows and implement browser features using WebKit.

Posts under WebKit tag

200 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

Instantiating "HTML-styled" NSAttributedString in a View's body produces weird behaviour
In an attempt to expose the capabilities of NSAttributedString in combination with UITextView to the world of SwiftUI (specifically the ability to render basic HTML), I've wrapped UITextView in a UIViewRepresentable and used that in a custom SwiftUI View. But I'm seeing some issues I can't really explain... So I would love to get a deeper understanding of what's going on. And possible also find a way to fix these issues in an appropriate way. This is how it goes: UIViewRepresentable wrapping UITextView to display NSAttributedString in the context of SwiftUI import SwiftUI struct AttributedText: UIViewRepresentable { private let attributedString: NSAttributedString init(_ attributedString: NSAttributedString) { self.attributedString = attributedString } func makeUIView(context: Context) -> UITextView { let uiTextView = UITextView() // Make it transparent so that background Views can shine through. uiTextView.backgroundColor = .clear // For text visualisation only, no editing. uiTextView.isEditable = false // Make UITextView flex to available width, but require height to fit its content. // Also disable scrolling so the UITextView will set its `intrinsicContentSize` to match its text content. uiTextView.isScrollEnabled = false uiTextView.setContentHuggingPriority(.defaultLow, for: .vertical) uiTextView.setContentHuggingPriority(.defaultLow, for: .horizontal) uiTextView.setContentCompressionResistancePriority(.required, for: .vertical) uiTextView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) return uiTextView } func updateUIView(_ uiTextView: UITextView, context: Context) { uiTextView.attributedText = attributedString } } Used in a custom HTML SwiftUI View import SwiftUI struct HTML: View { private let bodyText: String init(_ bodyText: String) { self.bodyText = bodyText } var body: some View { AttributedText((try? NSAttributedString( data: """ <!doctype html> <html> <head> <meta charset="utf-8"> <style type="text/css"> body { font: -apple-system-body; color: white; } </style> </head> <body> \(bodyText) </body> </html> """.data(using: .utf8)!, options: [ .documentType: NSAttributedString.DocumentType.html, .characterEncoding: NSUTF8StringEncoding, ], documentAttributes: nil )) ?? NSAttributedString(string: bodyText)) } } Put together in a simple SwiftUI app import SwiftUI struct ContentView: View { var body: some View { NavigationStack { ScrollView { HTML(""" <p>This is a paragraph</p> <ul> <li>List item one</li> <li>List item two</li> </ul> """) } .navigationTitle("HTML in SwiftUI") } } } @main struct MyApp: App { var body: some Scene { WindowGroup { ContentView() } } } Now, when I build and run the simple SwiftUI app shown above, it renders just fine, but there is a lot of log entries similar to "=== AttributeGraph: cycle detected through attribute 24504 ===". In addition to that, the navigation title bugs out when I scroll up. It also seems like SwiftUI is not able to detect changes to the HTML View, and does not re-evaluate its body if I re-create HTML with a new bodyText (even though its structural identity is preserved). When I use Instruments to inspect SwiftUI View body invocations, I can see that initiating the inline HTML styled NSAttributedString in the HTML View's body takes several milliseconds (not too surprising as it calls into WebKit stuff?), resulting in HTML.body taking more than 15 milliseconds to complete. Which is a lot more than if i just instantiated a "pure" text string using e.g the NSAttributedString(string:) initialiser. The initial render also seem to call HTML.body twice, a second time after calling the body of some View labeled "RootModifier" (Maybe the invocation of HTML.body took too long, and SwiftUI tries again?). Now, I acknowledge that all these signs yell "do not call computational heavy stuff inside a View's body!", but still, I would love to understand why SwiftUI complains about cycles in its AttributeGraph (as I can't really see any), and why SwiftUI does not re-evaluate HTML's body if I re-create HTML with a new bodyText (as HTML's initialiser is clearly called with a new and different bodyText value). I could also just completely drop the custom HTML SwiftUI View, and just use the AttributedText UIViewRepresentable directly. And then fully manage instances of HTML styled NSAttributedStrings in my model layer (and not instantiate them as part of some custom SwiftUI View). But that would remove some of the abstraction and readability of having a dedicated SwiftUI View for rendering HTML. So any suggestions on how to create such an abstraction/SwiftUI View would be greatly appreciated as well!
3
1
1.9k
Oct ’23
Webview, photolibrary,Camera, choose files
I have a safari webkit inside my mobile app which renders a webpage. The webpage has File upload option. when I click on it 3 options are shown as in screenshot. I am trying to make the safari kit to only allow Camera capture and hide Upload already existing files.  Is there any safari permission which I can remove to configuration that hide the options of upload from files.
2
0
1.2k
Jul ’23
How can I set up a web application server using swifter?
I have an angular project that I want to deploy using swifter in swiftUI. I have tried the below code. import Foundation import Swifter class WebServer { let server = HttpServer() let path: String init(path: String) { self.path = path } func startServer() { if let webAppPath = Bundle.main.path(forResource: "index", ofType: "html", inDirectory: "fileDir") { // Serve the Flutter web build files server["/"] = shareFilesFromDirectory(webAppPath) server["/hello"] = { .ok(.htmlBody("You asked for \($0)")) } do { try server.start(8081) print("Web server started on port 8081") } catch { print("Failed to start the web server: \(error)") } } else { print("Web app path not found.") } } func stopServer() { server.stop() print("Web server stopped") } } When trying this, web server is starting successfully, but when running baseUrl on localhost 8081, it gives 404. On "/hello", it does work correctly. I am sure all the files are there in the directory given in fileDir. Also I have tried running it using npm http-server, and it running correctly with it. Can someone point out the issue here? Or point me to any github repo which is achieving same with maybe a different package
0
0
561
Jul ’23
WebView Issues on iOS 16.4 and Above
Dear Community, We encountered a problem working with the webview. Working principle: the user navigates to the webview screen where a site is called that expects 3DSecure payment confirmation, the webview itself makes constant requests and polls the confirmation page (an example of the response we get, but we do not directly control these calls): { "code": "1", "data": { "response": { "success": "true" } } } { "code": "1", "data": { "response": { "thisTranId": "8034327064401", "mcpResponse": "<Envelope><Header><RequestId>8034327064401</RequestId></Header><Body><Invoke><ActionRs><ActionResult><Item Name=\"Status\">null</Item><Item Name=\"CRes\"><String>someString</String></Item></ActionResult></ActionRs></Invoke></Body></Envelope>" } } } If the application is not minimized, upon receiving confirmation, a POST request redirect occurs and the user successfully completes the flow. If during the receipt of confirmation, the application is minimized, upon returning the application to the foreground, the expected POST request with the redirect does not execute The problem only arose when users upgraded to iOS version 16.4 and above. Perhaps with the release of new versions, additional conditions have appeared and it is necessary to add some permission in the project settings or the webview itself? Best regards, Oleksii Baridn
0
0
471
Jul ’23
WKWebView sets CachePolicy unexpectedly to returnCacheDataElseLoad
We are using WKWebView to display a website (developed by us) in our app. When triggering a reload through code (e.g. when foregrounding the app), sometimes WKWebView will set the CachePolicy to returnCacheDataElseLoad. This means that WKWebView will then show outdated content from the user, since it will load the site content from local cache. We are not manually setting CachePolicy anywhere. Why does WKWebView decide to use returnCacheDataElseLoad? The server respons with the following cache-control header: public, max-age=9, s-maxage=2592000
0
1
577
Jul ’23
Authenticate with ProxyConfiguration in WebKit
Hello, I am trying to apply ProxyConfiguration on the WebKit webview. I've tried HTTP and SOCKSv5 without authentication, and it proxies normally. However, after I set up username authenticate on the server side, then set username and password with applyCredential on the client side, it couldn't authenticate the connection. Logs on the server side show the client side says it doesn't support any authentication. So I have some questions about the authentication: Does the ProxyConfiguration support authentication now? Does applyCredential apply credentials on HTTP/HTTPS and SOCKSv5 proxy?
4
1
809
Nov ’23
How to prevent repeated location alert with WKWebView in SwiftUI
I use Xcode 14.2 and my application is native iOS app, written in SwiftUI. I added below 2 keys for location permission. Privacy - Location Always and When In Use Usage Description Privacy - Location When In Use Usage Description On App launch, user see the first location prompt and select "Allow while using App". Later when user navigates to the we page which gets loading using WKWebView in SwiftUI by confirming the UIViewRepresentable like below struct WebView: UIViewRepresentable { var webView: WKWebView } WKWebView repeated asking for location alert, which is no use. Could you please guide how to suppress the WKWebView location alert? How to disable the location alert from Webkit? I tried the this approach, but not worked. Thanks & Regards, Pravin D
0
0
374
Jul ’23
WebRTC IceCandidates seems weird
Hi there, I'm currently making a web application using webRTC. Even though all the SDP info and ICES of caller, callee are well transmitted, the connection was kept failing. The other devices are functioning well. However at just Iphone(13), it's not working. I tried to connect at same network. And it's working. Therefore I think it's a problem about Ice candidates. I read similar post to avoid this issue. And when one of safari's advanced option called WebRTC platform UDP sockets is disabled it's working. Is there a way that I can connect without tuning options of Safari? Thanks. FYI this is one of my Iphone's Ice candidate:842163049 1 udp 1685921535 118.235.10.100 50750 typ srflx raddr 0.0.0.0 rport 50750 generation 0 ufrag 7e7f network-id 3 network-cost 900
0
1
456
Aug ’23
Some content and features locked in with authentication via webview
What policy does apply to me as I have a working application that serves contents based on their chosen location or place which does not require any login but for some super users I will open webview where they can authenticate themself and view/change thee only do I still need to provide test credential and any policy that apply to me related to data and privacy as the content will be also shown via webview and my app only served to US region.
0
0
389
Aug ’23
App Privacy Report "context" & "contextVerificationType"
Hey all, recently I've been inspecting my app's privacy report to look for any unexpected hosts. One thing I've noticed is that for hosts that seem unexpected, like the facebook example below, the context field is populated. For all expected requests, it is an empty string. Here in the docs, context and contextVerificationType are not described in much detail. Could someone provide me with some more detail on how those fields are populated? My assumption is that these domains are coming from embedded WKWebView's, but I'm not sure how those fields are populated so I cannot confirm my suspicion. Is it possible to have context poopulated without being in a webview? { "timeStamp":"2023-08-08T09:37:33.456-04:00", "initiatedType":"AppInitiated", "context":"facebook.com", "domain":"www.facebook.com", "contextVerificationType":2, "type":"networkActivity", "domainType":1, "firstTimeStamp":"2023-08-08T09:37:33.456-04:00", "bundleID":"my-bundle-id", "domainOwner":"Facebook, Inc.", "hits":1, "domainClassification":1 } Thanks! LMK if I can add any additional details or if this has been discussed before.
0
0
463
Aug ’23
WKWebView doesn't show edit menu for pdf documents.
I've noticed that edit menu with Copy, Look Up, Translate options doesn't appear when I select some text in pdf file loaded in WKWebView. This issue is reproducible on the latest iOS 16 and 17 beta versions. Additionally, these two WKUIDelegate methods are called for all types of documents except PDFs. func webView(_ webView: WKWebView, willPresentEditMenuWithAnimator animator: UIEditMenuInteractionAnimating) func webView(_ webView: WKWebView, willDismissEditMenuWithAnimator animator: UIEditMenuInteractionAnimating) Is this a bug or is there a new WebKit/PDFKit API I could use to enable this menu? Feedback ID: FB12759407
0
0
522
Aug ’23
Keyboard extension's WKWebView not running in iOS16
My code used to work on iOS versions prior to iOS16, but it no longer does. I am running iPadOS -not checked on iPhones. A view based on WKWebView is supposed to show HTML files inside a keyboard extension. It is now not not displaying correctly and any Javascript will lock up the UI. Setting stringByEvaluatingJavaScriptFromString > evaluateJavaScript's completionHandler to nil fixes this, but Javascript is not run. No errors, but blank space. Any ideas?
1
0
557
Aug ’23
WKWebView don't work for https+websocket with self-signed certificate
Following works fine for https url with self signed certificate. But it does not work for secured websocket protocol with self signed ceritificate (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler { NSURLCredential * credential = [[NSURLCredential alloc] initWithTrust:[challenge protectionSpace].serverTrust]; completionHandler(NSURLSessionAuthChallengeUseCredential, credential); } }
0
0
306
Aug ’23
Is there a way to persist state in an iOS Progressive Web App
Hello! I've been using iOS's new Web Push support for progressive web apps. I am running into an issue where any state I store in the service worker (via postMessage from the main thread) gets lost while the PWA is swiped away and not running in the foreground. My use case is I when I receive the push notification, I want to use some of that state. It seems the service worker running in the background loses context of that state. I tried working around this via Cache Storage and IndexedDB but those also seem to be lost. I was wondering if anyone has a good way to store state while the PWA app isn't in the foreground?
1
0
728
Aug ’23
How to check origin URL when loading a web archive inside WKWebView
Introduction We load a saved web archive (NSData) into a WKWebView instance using following method: [self.webView loadData: archive MIMEType:@"application/x-webarchive" characterEncodingName:@"" baseURL:baseURL]; We also have some custom scripts injected safely into the web view using following snippet WKUserScript *userScript = [[WKUserScript alloc] initWithSource:<jsfilename> injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:onMainFrame]; [webview.configuration.userContentController addUserScript:userScript]; Things work fine till here. Observation When I get the call from Javascript Layer to the native iOS Layer via: -[CustomWebViewController userContentController:didReceiveScriptMessage:] the value message.frameInfo.request.URL.absoluteString is "about:blank" Question Is there any way to get the original baseURL back here? Alternatives Considered: Store the baseUrl in an instance variable and use it here but since we are using this url to determine if the request is coming from valid domains, having a url from this method will be most credible. We are looking a way to piggyback this info (baseURL domain) to our web archive at the time of loading it in webView and extract that from the -[CustomWebViewController userContentController:didReceiveScriptMessage:] method Any help will be much appreciated.
0
0
563
Aug ’23
Associate user with Instagram Basic Display API response
Hi, I am developing an app that allows users to connect their Instagram accounts. However, I have a problem that someone may be able to help with. So, in the app(frontend) the web view is presented with the specific URL that Instagram API requires to receive the necessary access token, in the URL you have to supply a redirect_uri which is where Instagram will send its response (authorization_code), you then have to send that authorization code to another Instagram URL to which it will respond with the token, I need to store this token in my database. The redirect_uri is a domain that points to my backend server, and I have to keep it this way since Instagram requires that the redirect_uri uses HTTPS. Finally the problem, I don't know how to associate the authorization code with the user that made the request since Instagram acts as a middleman between the front-end and back-end. Upon signup, each user is assigned a user ID. I tried to find a way to include that in the redirect_url with no luck. Thanks in advance!
0
0
251
Aug ’23
WebKit mic permission
Hello. I'm trying to stop my webview from asking for mic permission every time it runs, as I already handle it out in the application. This is the code I use, but it doesn't seem to run the code for the permission at all (tried printing and debugging, and it doesn't go into it at all). I am using visionOs if that makes a difference. Are there any alternative solutions to this? import SwiftUI import WebKit import AVFAudio struct WebView: UIViewRepresentable { let urlString: String func makeUIView(context: Context) -> WKWebView { let webView = WKWebView() webView.navigationDelegate = context.coordinator if let url = URL(string: urlString) { let request = URLRequest(url: url) webView.load(request) } return webView } func updateUIView(_ uiView: WKWebView, context: Context) {} func makeCoordinator() -> Coordinator { Coordinator() } class Coordinator: NSObject, WKUIDelegate, WKNavigationDelegate { // Handle media capture permissions func webView(_ webView: WKWebView, decideMediaCapturePermissionsFor origin: WKSecurityOrigin, initiatedBy frame: WKFrameInfo, type: WKMediaCaptureType) async -> WKPermissionDecision { return .grant } // Handle URL authentication challenges func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { let username = "user" let password = "password" let credential = URLCredential(user: username, password: password, persistence: .forSession) completionHandler(.useCredential, credential) } } } struct AssistantView: View { @Environment(\.dismissWindow) private var dismissWindow var body: some View { WebView(urlString: "https://somelink.com/") .edgesIgnoringSafeArea(.all) .frame(width: 500, height: 800) } }
1
0
648
Aug ’23
Safari / IOS WKWebView waiting for 250 bytes before rendering content. Delaying execution of external .js
We have observed that safari / IOS WKWebView waits for ~250 renderable bytes before it starts to render HTML as received in the browser. This is different from the behaviour in chrome / Firefox where they start to render the content as soon as the first byte is available. We wanted to know if there’s a way to stop this behaviour, to bring parity across the two browsers. Also is there any official doc / announcement establishing this limit of ~250 characters which we’ve found experimentally. Details of our experiment: We created a simple HTTP Server In python that: Emits a stream of (100) characters Sleeps for 3 seconds Emits some more characters You can find the experiment code towards the end of the post The ideal behaviour (which we observed in chrome) is that the content should be rendered progressively. Implying that the 100 characters before the .sleep() should be rendered immediately. This should be followed by a gap of 3 seconds, and then the rest of the characters are displayed. You can see this behaviour where I reload the page: https://drive.google.com/file/d/1JD_ZbghX3OOz_CpSR2iGn_Se7_2HDRTw/view?usp=sharing The text "Before Sleep" followed by a line of 100 dashes is the content before .sleep() which appears immediately. The text “After Sleep” apears after a time of 3 seconds which is the desired behaviour. In safari however, all of the content appears at once after 3 seconds, instead of being rendered progressively as seen here when I reload the page : https://drive.google.com/file/d/1RiD7eFuwGYL5lGIcU1CF2gcpoSysHJ9C/view?usp=sharing However if we increase the number of characters emitted before the .sleep() to ~250 (256 to be exact) safari behaves similar to chrome and the content appears progressively. You can see this here on page reload : https://drive.google.com/file/d/1JlHFbZPdFuIiaAlkgYCWo61amBIysKL1/view?usp=sharing We found this value of ~250 experimentally via hit and trial. Not only rendering, we also found this delaying the execution of external .js (that is present in a separate .js file embedded via tag). To prove this, I added a simple console.log(performance.now()) at the top of the .js file (outside of any function). In safari with artificially inserted ~260 extra bytes this log was printed at ~1 - 1.5 secs. However without the extra bytes this log came at ~3 - 4 seconds. This gap corresponds to the time taken to make rest of the page ready. Note that embedded .js present in the HTML was not affected by this. Wondering if other’s have faced the same problem and if there's an official doc stating this limit / any workarounds. Python HTTP Server code that we executed for our experiment from http.server import BaseHTTPRequestHandler, HTTPServer import time import sys class handler(BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.send_header('Content-type','text/html') self.end_headers() self.flush_headers() # Content before .sleep() message = f' <!DOCTYPE html> \ <html lang="en"> \ <head> \ <meta charset="UTF-8"> \ <title></title> \ </head> \ <body> \ ' self.wfile.write(bytes(message, "utf8")) self.wfile.flush() beforeSleep = 'Before Sleep' self.wfile.write(bytes(beforeSleep, "utf8")) self.wfile.flush() lineBreak = '<br/>' self.wfile.write(bytes(lineBreak, "utf8")) self.wfile.flush() blankChar = "-" fillerString = ""; for x in range(260): fillerString = fillerString + blankChar self.wfile.write(bytes(fillerString, "utf8")) # Sleep for 3 seconds time.sleep(3) self.wfile.write(bytes(lineBreak, "utf8")) self.wfile.flush() # Content after .sleep() which should be rendered progressively afterSleep = "After Sleep" self.wfile.write(bytes(afterSleep, "utf8")) self.wfile.flush() with HTTPServer(('', 8000), handler) as server: server.serve_forever()```
0
0
553
Aug ’23