Explore the integration of web technologies within your app. Discuss building web-based apps, leveraging Safari functionalities, and integrating with web services.

General Documentation

Post

Replies

Boosts

Views

Activity

Custom Scrollbar Styles Not Updating Dynamically in Safari
I am currently working on a web application where I need a container/textarea with overflow on the y-axis. I've managed to achieve the desired scrollbar customization in Chrome by dynamically adding a class to the DOM when a scroll event occurs. This class modifies the "::-webkit-scrollbar" pseudo-element, allowing me to toggle between different styles as needed. However, I'm facing an issue with Safari. Despite my custom styles being visible in the DOM when inspected, the scrollbar styles do not update dynamically as they do in Chrome. Expected Behavior: The custom scroll bar should be hidden when the user is not scrolling. When the user begins to scroll, the scroll bar should become visible.
0
0
44
19h
Safari 17.4> Web push Api Broken?
Hi, We are having issues trying to get the Web Push API working in Safari 17.4/5 and 18 beta (both on ios and macos). Below you can find a basic vanilla web app that tests push subscription (its not wired up to actual service but does allow testing the client side). It has been configured as per the push api requirements.. Runs over https (using self signed certificate). Runs a service worker. Asks for permissions via button click before subscribing. Running this on macos chrome/firefox works fine, after providing permissions the promise returns a populated subscription object (Endpoint / p256dh values etc). When reloading the site, the subscription is persisted. In Safari, the permissions are requested as per other browsers but the returned subscription promise object (see code below) (https://developer.mozilla.org/en-US/docs/Web/API/PushSubscription), although the keys are present, contains all empty values. Calling GetKeys for any of the values, also returns empty. On reload, it is clear that a subscription was not actually created. serviceWorkerRegistration.pushManager.subscribe(subscribeOptions).then(function (subscription) { return subscription; }).catch(function (error) { console.log('Error:', error); throw error; }); The subscription promise does not catch, so why these values are empty is a mistry. The same code works fine on other browsers on the same OS, so it seems there is an underlying issue in safari's implementation of the web push api. Can you please run the sample app and confirm if you see the same issue, together with any required changes if it's a coding issue, or, confirm that this is a safari bug? If its a limitation due to config etc, then the catch should be thrown with a useful error. Github repo.. https://github.com/andyball89/web-push-testing Thanks, Andy
0
1
112
1d
Does safari(17) on mac support Largeblob Authentication extensions
Hi: I saw the post WWDC WebKit release notes said Safari will support largeblob extension from version 17. But when I create a credential with largeblob extension, different action takes according what authenticator used. The credential options is: "credCreateOptions": { "rp": { "name": "WebAuthn demo", "id": "webauthn.turinggear.com" }, "user": { "name": "Jonathon.Runte97@gmail.com", "displayName": "Jonathon.Runte97@gmail.com", "id": "bqShD9YGRicjM-1foXiBqrdCzTHTuG1bkmKaxzn7oEM" }, "challenge": "9BP4y2epk2b3MhRCRRS5tt4bdWYLPJcKBLMMiB_7p7E", "pubKeyCredParams": [ { "alg": -7, "type": "public-key" }, { "alg": -257, "type": "public-key" } ], "excludeCredentials": [], "authenticatorSelection": { "requireResidentKey": true, "residentKey": "required", "userVerification": "discouraged" }, "attestation": "none", "extensions": { "credProps": true, "largeBlob": { "support": "preferred" } } } When i choose use iPhone be my authenticator, it seems that largeblob act as it should be: "credential" : { "id": "ZRxBdH4LKE4eiVxbwcA4Kmn9VZk", "rawId": "ZRxBdH4LKE4eiVxbwcA4Kmn9VZk", "response": { "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViYSETDPyxegNfyH_fI_8t9iVRDn34LxYd8YH1k2u4xSk5dAAAAAPv8MAcVTk7MjAtuAgVX170AFGUcQXR-CyhOHolcW8HAOCpp_VWZpQECAyYgASFYICY6gkqg6OG_v1BlGCPj7gSwsu_c0vTmVzmfd7TsqEh5Ilgg_Cn0mAiO8QCx7J1xw809VBq8iI-U5pgY0I947B7XF9g", "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiOVcta3RMbEswemZDSXpFb2hNd3E3OTgxQXJlRzV0aEVBdmRHdXNHcUsxcyIsIm9yaWdpbiI6Imh0dHBzOi8vd2ViYXV0aG4udHVyaW5nZ2Vhci5jb20ifQ", "transports": [ "internal", "hybrid" ], "publicKeyAlgorithm": -7, "publicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJjqCSqDo4b-_UGUYI-PuBLCy79zS9OZXOZ93tOyoSHn8KfSYCI7xALHsnXHDzT1UGryIj5TmmBjQj3jsHtcX2A", "authenticatorData": "SETDPyxegNfyH_fI_8t9iVRDn34LxYd8YH1k2u4xSk5dAAAAAPv8MAcVTk7MjAtuAgVX170AFGUcQXR-CyhOHolcW8HAOCpp_VWZpQECAyYgASFYICY6gkqg6OG_v1BlGCPj7gSwsu_c0vTmVzmfd7TsqEh5Ilgg_Cn0mAiO8QCx7J1xw809VBq8iI-U5pgY0I947B7XF9g" }, "type": "public-key", "clientExtensionResults": { "largeBlob": { "supported": true } }, "authenticatorAttachment": "platform" } Safari returns clientExtensionResults.largeBlob.supported= ture. But when I use an NFC authenticator with the same credCreateOptions, safari didnot return clientExtensionResults section. Response as follows(ignore the challenge and others random data): "credential" : { "id": "uEVMzgsINXj7bHFD5Z5xbMGJ7k6tnrMQSLjB4yB8_0GxbUPoWYUYX8E3D9XB24Cv-PMh6cRpCFt5klUHqsot2Yc48BVu5TN8sbabTgped2x46ljdsxFzaNCA8D2y9FZK8BHLLZTKHNuzJw4SCYUkzg", "rawId": "uEVMzgsINXj7bHFD5Z5xbMGJ7k6tnrMQSLjB4yB8_0GxbUPoWYUYX8E3D9XB24Cv-PMh6cRpCFt5klUHqsot2Yc48BVu5TN8sbabTgped2x46ljdsxFzaNCA8D2y9FZK8BHLLZTKHNuzJw4SCYUkzg", "response": { "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVj0SETDPyxegNfyH_fI_8t9iVRDn34LxYd8YH1k2u4xSk5FAAABeAAAAAAAAAAAAAAAAAAAAAAAcLhFTM4LCDV4-2xxQ-WecWzBie5OrZ6zEEi4weMgfP9BsW1D6FmFGF_BNw_VwduAr_jzIenEaQhbeZJVB6rKLdmHOPAVbuUzfLG2m04KXndseOpY3bMRc2jQgPA9svRWSvARyy2UyhzbsycOEgmFJM6lAQIDJiABIVggg2LXO5Q2U0ETrSxrLKxCfKKCTCitTCx9bpxD1Gw917ciWCDsxnw4Wd7M_UTiGQJ7swCMXN83nprsT8wkTlftXRizmw", "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiOUJQNHkyZXBrMmIzTWhSQ1JSUzV0dDRiZFdZTFBKY0tCTE1NaUJfN3A3RSIsIm9yaWdpbiI6Imh0dHBzOi8vd2ViYXV0aG4udHVyaW5nZ2Vhci5jb20ifQ", "transports": [ "nfc" ], "publicKeyAlgorithm": -7, "publicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEg2LXO5Q2U0ETrSxrLKxCfKKCTCitTCx9bpxD1Gw917fsxnw4Wd7M_UTiGQJ7swCMXN83nprsT8wkTlftXRizmw", "authenticatorData": "SETDPyxegNfyH_fI_8t9iVRDn34LxYd8YH1k2u4xSk5FAAABeAAAAAAAAAAAAAAAAAAAAAAAcLhFTM4LCDV4-2xxQ-WecWzBie5OrZ6zEEi4weMgfP9BsW1D6FmFGF_BNw_VwduAr_jzIenEaQhbeZJVB6rKLdmHOPAVbuUzfLG2m04KXndseOpY3bMRc2jQgPA9svRWSvARyy2UyhzbsycOEgmFJM6lAQIDJiABIVggg2LXO5Q2U0ETrSxrLKxCfKKCTCitTCx9bpxD1Gw917ciWCDsxnw4Wd7M_UTiGQJ7swCMXN83nprsT8wkTlftXRizmw" }, "type": "public-key", "clientExtensionResults": {}, "authenticatorAttachment": "cross-platform" } Even without a clientExtensionResults.largeBlob.supported= false. According to w3c, it should return clientExtensionResults.largeBlob.supported= false ? The NFC authenticaor do support largeblob extensions and act write with the same credCreateOptions on edge on windows. Does safari need some extra parameters? My safari is the newest version of 17.5 (19618.2.12.11.6), mac version is Sonoma 14.5(23F79). Thank you very much.
0
0
68
2d
Issue with unlimitedStorage permission in manifest.json for Safari Extension on iOS 18 Beta
Here is my manifest.json: "permissions": [ "<all_urls>", "storage", "unlimitedStorage", "activeTab" ] Despite declaring "unlimitedStorage" in permissions, I encounter the following error when trying to save files exceeding approximately 3MB: Invalid call to browser.storage.local.set(). Exceeded storage quota. This issue did not occur in iOS 17. Is this behavior intentional or is it a bug? If it is intentional, is there an alternative method to handle large file?
1
0
132
2d
iOS Safari Website Data and JavaScript
HI, Just discovered... If I update - change function arguments the JavaScript on my website fails as Safari keeps the previous version and doesn't notice that the JavaScript is new (only a very small change - one function now has two arguments instead of one, then calls AJAX). Is there a way to force a reloading of the JavaScript? This is really annoying. After much checking around (and not much sleep) on various other devices and browsers I finally cleared iOS Safari's history, and all is well. I don't know if others have had this problem, but I thought I'd post it here anyway. I always automatically clear the browser history when closing or re-launching the browser during development of web stuff. Regards, Paul
0
0
123
3d
WKWebView OAuth popup misses window.opener in iOS 17.5+
In my project, I'm using the WKWebView to display the Google OAuth popup. And after it appears, the JS window.opener is null, and because of that the original window cannot receive an auth token in a callback. This works perfectly fine in iOS 17.0 and earlier, but broken starting from 17.5. I've tested on the 18.0 too - same results the opener is always null no matter what I try. Web Part: <html> <head> <script src="https://accounts.google.com/gsi/client" async></script> </head> <script type="text/javascript"> var global = {}; window.addEventListener("message", function (ev) { console.log("=== Event Listener ==="); console.log(ev); }); function client() { if (global.client === undefined) { global.client = google.accounts.oauth2.initTokenClient({ client_id: '<client_id>', scope: 'https://www.googleapis.com/auth/userinfo.email \ https://www.googleapis.com/auth/userinfo.profile', callback: function (responseToken) { console.log("=== Callback ==="); console.log(responseToken); let auth = document.getElementById("auth"); auth.textContent = 'Token: ' + responseToken.access_token } }); } return global.client; } function googleOauthClient() { console.log("Trying to login"); let auth = document.getElementById("auth"); auth.textContent = 'In Progress...'; client().requestAccessToken(); } </script> <body> <div id="center"> <a onclick="googleOauthClient()"><h1>Google Login Attempt</h1></a> <h2 id="auth"></h2> </div> </body> </html> In iOS showing popup is implemented as: #import "WebViewController.h" #import "SafariServices/SafariServices.h" @interface WebViewController () <WKNavigationDelegate, WKUIDelegate> { WKWebView *web; WKWebView *popupWebView; } @end @implementation WebViewController - (void)loadView { [super loadView]; WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; [[config preferences] setJavaScriptEnabled:YES]; [[config preferences] setJavaScriptCanOpenWindowsAutomatically:NO]; [[config defaultWebpagePreferences] setAllowsContentJavaScript:YES]; [config setAllowsInlineMediaPlayback:YES]; web = [[WKWebView alloc] initWithFrame:self.view.frame configuration:config]; [web setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; [web setAllowsLinkPreview:YES]; [web setNavigationDelegate:self]; [web setUIDelegate:self]; [web setInspectable:YES]; [web setCustomUserAgent:[[web valueForKey:@"userAgent"] stringByAppendingString:@" Safari"]]; [[self view] addSubview:web]; } - (void)viewDidLoad { [super viewDidLoad]; NSString *url = @"http://localhost:8080/oauth"; // A simple Spring Boot App where HTML is hosted [web loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]]]; } - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { decisionHandler(WKNavigationActionPolicyAllow); } - (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures { if (navigationAction.targetFrame == nil) { popupWebView = [[WKWebView alloc] initWithFrame:webView.frame configuration:configuration]; [popupWebView setCustomUserAgent: [webView customUserAgent]]; [popupWebView setUIDelegate:self]; [popupWebView setNavigationDelegate:self]; [popupWebView setInspectable:true]; [[[popupWebView configuration] defaultWebpagePreferences] setAllowsContentJavaScript:YES]; [[[popupWebView configuration] preferences] setJavaScriptCanOpenWindowsAutomatically:NO]; [[[popupWebView configuration] preferences] setJavaScriptEnabled:YES]; [[popupWebView configuration] setSuppressesIncrementalRendering:YES]; [webView addSubview:popupWebView]; [popupWebView loadRequest:[navigationAction.request copy]]; return popupWebView; } return nil; } - (void)webViewDidClose:(WKWebView *)webView { [webView removeFromSuperview]; popupWebView = nil; } - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error { NSLog(@"didFailProvisionalNavigation: %@", [error description]); } - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error { NSLog(@"didFailNavigation: %@", [error description]); } - (void)webView:(WKWebView *)webView requestMediaCapturePermissionForOrigin:(WKSecurityOrigin *)origin initiatedByFrame:(WKFrameInfo *)frame type:(WKMediaCaptureType)type decisionHandler:(void (^)(WKPermissionDecision decision))decisionHandler { decisionHandler(WKPermissionDecisionGrant); } - (void)webView:(WKWebView *)webView requestDeviceOrientationAndMotionPermissionForOrigin:(WKSecurityOrigin *)origin initiatedByFrame:(WKFrameInfo *)frame decisionHandler:(void (^)(WKPermissionDecision decision))decisionHandler { decisionHandler(WKPermissionDecisionGrant); } - (void)webView:(WKWebView *)webView runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSArray<NSURL *> * _Nullable URLs))completionHandler { completionHandler(nil); } @end What I tried so far: Disabled in the Settings -> Safari -> Cross-Origin-Opener-Policy; Disabled popup blocking; Added Allow Arbitrary Loads to Info.plist (actually added all possible security policies); This actually works in the SFSafariViewController, but I cannot use it because I need to hide the Navigation and Status bar panels. How it looks in 17.0 vs 17.5 And ideas about what I might be doing wrong? Or maybe some workaround I can utilize to get the Google OAuth token from the popup. Thanks in advance.
0
0
149
3d
How can I initiate a new URL request using an already loaded WKWebView?
I have opened a WKWebView with "https://www.google.com", for example. After that, I put my app in the background. When I try to open a new URL request in the same web view with a new link, like "https://www.youtube.com", by redirecting the app, it doesn't work. I've put a listener function to detect the new URL and called "webView.load(newURLRequest)". However, the web view never loads the new URL request. I have tried to solve this issue by adding a delay through DispatchQueue. Adding a delay sometimes works and sometimes doesn't. I also tried using NavigationDelegate, but it still didn't help. Below is my code: override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(self.refreshData), name: NSNotification.Name("refreshweb"), object: nil) } @objc func refreshData() { let url = URL (string: "https://www.youtube.com") let requestObj = URLRequest(url: url!) webview.load(requestObj) }
0
0
79
5d
I am unable to debug a Safari extension in Xcode 16 beta 1 on iOS 18 beta
The process to reproduce: Create a new extension with Xcode 16 beta. Run it on the simulator or any iOS device with iOS 18 beta installed. Open Safari on your Mac, then open Develop -> Device you use. Unable to access these menus for iOS 18: 1. Extension - Extension Background Content 2. Extension - Extension Pop-up Page - popup.html However, for iOS 17.4, I can see these options in Develop -> Device iOS(17.4). I have another extension on the App Store that is not working on the iOS 18 beta, but I am unable to debug it because of this issue.
1
0
216
1w
Microphone input in website in webview for swiftui
I have a website of a chatbot that also accepts microphone inputs and I embedded that into my swift and the everything other than the mic is working. I have included permission in the info file and here’s some of the code : import SwiftUI import WebKit import AVFoundation struct WebView: UIViewRepresentable { let url: URL class Coordinator: NSObject, WKUIDelegate, WKNavigationDelegate { var parent: WebView init(parent: WebView) { self.parent = parent } func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { decisionHandler(.allow) } func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) { let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in completionHandler() })) parent.presentAlert(alert: alert) } func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) { let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in completionHandler(true) })) alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { _ in completionHandler(false) })) parent.presentAlert(alert: alert) } func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) { let alert = UIAlertController(title: prompt, message: nil, preferredStyle: .alert) alert.addTextField { textField in textField.text = defaultText } alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in completionHandler(alert.textFields?.first?.text) })) alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { _ in completionHandler(nil) })) parent.presentAlert(alert: alert) } func webView(_ webView: WKWebView, requestMediaCapturePermissionFor origin: WKSecurityOrigin, initiatedByFrame frame: WKFrameInfo, type: WKMediaCaptureType, decisionHandler: @escaping (WKPermissionDecision) -> Void) { decisionHandler(.grant) } func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { print("Navigation error: \(error.localizedDescription)") } } func makeCoordinator() -> Coordinator { Coordinator(parent: self) } func makeUIView(context: Context) -> WKWebView { let webConfiguration = WKWebViewConfiguration() webConfiguration.allowsInlineMediaPlayback = true webConfiguration.mediaTypesRequiringUserActionForPlayback = [] let webView = WKWebView(frame: .zero, configuration: webConfiguration) webView.uiDelegate = context.coordinator webView.navigationDelegate = context.coordinator requestMicrophoneAccess() return webView } func updateUIView(_ webView: WKWebView, context: Context) { let request = URLRequest(url: url) webView.load(request) } func requestMicrophoneAccess() { AVAudioSession.sharedInstance().requestRecordPermission { granted in DispatchQueue.main.async { if granted { print("Microphone access granted") } else { print("Microphone access denied") } } } } func presentAlert(alert: UIAlertController) { if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let rootViewController = windowScene.windows.first?.rootViewController { rootViewController.present(alert, animated: true, completion: nil) } } }
0
0
171
1w