I’m experiencing a crash in WKWebView on iOS 26 Developer Beta 5 and Beta 6 with the following exception:
CALayer position contains NaN: [nan 65]
The crash occurs when the following CSS properties are applied to content displayed in WKWebView:
-webkit-user-select: none;
-webkit-touch-callout: none;
This issue happens consistently whenever these styles are set, leading to the crash inside WKWebView.
Is this a known bug in the current iOS 26 betas, or is there a recommended workaround?
General
RSS for tagExplore the integration of web technologies within your app. Discuss building web-based apps, leveraging Safari functionalities, and integrating with web services.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I'm working on a regular website, in which I'm trying to debug using the (MacOS) Safari Development tools. Since updating my Simulator to iOS 16.4, console.log is no longer displayed in the Console. Even when executing it directly in the console (console.log('test');), it's not printed.
Now, I've read that this is a feature for debugging in-app browser content (https://webkit.org/blog/13936/enabling-the-inspection-of-web-content-in-apps/) but can't find the regular web workaround here.
TL;DR: No longer see console.log in iOS 16.4 Safari when debugging from my Mac.
Thanks in advance for your suggestions!
version:26.0-23A5308g
Terminating app due to uncaught exception 'CALayerInvalidGeometry', reason: 'CALayer position contains NaN: [nan 103.667]. Layer: <CALayer:0x15793f720; position = CGPoint (0 0); bounds = CGRect (0 0; 0 48); delegate = <_UIEditMenuListView: 0x10f962f80; frame = (nan 0; 0 48); anchorPoint = (inf, 0); alpha = 0; layer = <CALayer: 0x15793f720>>; sublayers = (<CALayer: 0x155964e70>, <CALayer: 0x15793eaf0>); opaque = YES; allowsGroupOpacity = YES; anchorPoint = CGPoint (inf 0); opacity = 0>'
Topic:
Safari & Web
SubTopic:
General
Hello everyone,
I'm working on an app that uses WKWebView.
My app uses a custom menu and we disable the default menu by overriding with:
WKWebAction.canPerformAction()
However, with the new iOS 18.2 release, I am no longer able to override the "Copy Link with Highlight" option that pops up when highlighting a selection as can be seen from the screenshot:
Has anyone found a work around/bypass for this?
Environment
iOS Version: iOS 18.2
Device: iPhone 13 Pro
App platform: iOS
Xcode version: 16.1
MacOS: 14.5
I'm converting a Chrome Extension to a Safari Web Extension, I found it's not easy to get favicon of current tab/url natively.
The tab object in Safari doesn't have favIconUrl.
{
	"id": 121,
	"index": 6,
	"active": true,
	"width": 1324,
	"audible": false,
	"url": "https://github.com/",
	"mutedInfo": {
		"muted": false
	},
	"windowId": 2,
	"title": "GitHub",
	"incognito": false,
	"pinned": false,
	"height": 935,
	"highlighted": true,
	"status": "complete"
}
		
2. I didn't find Safari has similar thing like chrome://favicon
3. I found Safari's favicon caches in ~/Library/Safari/Favicon Cache/favicons but have no idea how to use them in Safari Web Extension.
On my native app, will open a wkWebview to display some content. And it will crash on iOS26:
*** Terminating app due to uncaught exception 'CALayerInvalidGeometry', reason: 'CALayer position contains NaN: [nan 103.667]. Layer: <CALayer:0x14c2457d0; position = CGPoint (0 0); bounds = CGRect (0 0; 0 48); delegate = <_UIEditMenuListView: 0x14c273980; frame = (nan 0; 0 48); anchorPoint = (inf, 0); alpha = 0; layer = <CALayer: 0x14c2457d0>>; sublayers = (<CALayer: 0x1306320a0>, <CALayer: 0x14c245a70>); opaque = YES; allowsGroupOpacity = YES; anchorPoint = CGPoint (inf 0); opacity = 0>'
Since probably the late iOS 17.4.x, 17.5.1 and still now in 17.6 beta our extension has been experiencing issues with the accompanying background script or service worker being permanently killed with no warning after about 30-45 seconds after initial installation (installation, not page load!).
In all other browsers (including Safari on MacOS) unloading the service worker is part of the normal lifecycle to save memory and CPU if it is idle. In our extension the service worker is used only during the first 5-10 seconds of every page visit, so we are used to seeing it unload after that and consider this a good thing. However, normally, the service worker is able to wake back up when needed - which is no longer the case in iOS.
Once dead, nothing a normal user would do can wake the service worker back up:
No events like webNavigation or similar will trigger anymore
Any attempt to call sendMessage to it from a content-script also does not wake up the service worker and instead returns undefined to the content script immediately
Closing and opening Safari does not start it again
The only two things that will give the service worker another 30-40 seconds of life is a reboot of the device or disabling and then re-enabling the extension. During those few second the extension is working perfectly.
There are no errors or indications in the logs of what is going on and the extension works just fine in Chrome, Firefox, Edge as well as Safari on MacOS and Safari in the Mobile simulator. Only actual iOS devices fail.
It seems like a temporary workaround is to change the manifest to not load the service worker as a service worker by changing
"background": {
"service_worker": "service.js"
}
to
"background": {
"scripts": ["service.js"],
"persistent": false
}
With this change (courtesy of https://forums.developer.apple.com/forums/thread/721222) the service worker is still unloaded but correctly starts up again when needed. Having to make this change does not seem to be consistent with manifest v3 specs though (see this part in Chrome’s migration guide as an example: https://developer.chrome.com/docs/extensions/develop/migrate/to-service-workers#update-bg-field).
According to the release notes of 17.6 beta this bug was supposedly fixed:
“Fixed an issue where Safari Web Extension background pages would stop responding after about 30 seconds. (127681420)”
However, this bug is not fixed - or at least not entirely fixed. It seems to work better for super simple tests doing nothing but pinging the service worker from the content script, but for the full blown extension there is no difference at all between 17.5.1 and 17.6.
Has there been a change in policy about service workers and background scripts for Safari in iOS?
Are anyone else seeing this issue?
Also seemingly related:
https://forums.developer.apple.com/forums/thread/756309
https://forums.developer.apple.com/forums/thread/750330
https://developer.apple.com/forums/thread/757926
https://forums.developer.apple.com/forums/thread/735307
Hi there!
I'm new to App Development and I'm running into the following error when playing audio on a website loaded through a WKWebView:
0x112000cc0 - ProcessAssertion::acquireSync Failed to acquire RBS assertion 'WebKit Media Playback' for process with PID=70.197, error: Error Domain=RBSServiceErrorDomain Code=1 "(originator doesn't have entitlement com.apple.runningboard.assertions.webkit AND originator doesn't have entitlement com.apple.multitasking.systemappassertions)" UserInfo={NSLocalizedFailureReason=(originator doesn't have entitlement com.apple.runningboard.assertions.webkit AND originator doesn't have entitlement com.apple.multitasking.systemappassertions)}
Looking through this forum, it seems more people have this issue, yet no one has found a solution (or posted it...). The solutions that I did find (Background Modes capability, webView.configuration.allowsInlineMediaPlayback = true), did nothing.
To make sure the issue had nothing to do with my own code, I created an empty project to reproduce the issue. I'm not sure on the best way to share it, but it's a small file (forgive me, I have no clue what it does, actually chatGPT made it for me. My real application is a WebApp wrapped with Capacitor, so it handles all the Swift stuff)
import SwiftUI
import WebKit
struct WebView: UIViewRepresentable {
let urlString: String
func makeUIView(context: Context) -> WKWebView {
let webView = WKWebView()
webView.configuration.allowsInlineMediaPlayback = true
webView.configuration.allowsAirPlayForMediaPlayback = true
webView.navigationDelegate = context.coordinator
return webView
}
func updateUIView(_ uiView: WKWebView, context: Context) {
if let url = URL(string: urlString) {
let request = URLRequest(url: url)
uiView.load(request)
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, WKNavigationDelegate {
var parent: WebView
init(_ parent: WebView) {
self.parent = parent
}
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
print("Web page loading failed: \(error.localizedDescription)")
}
}
}
struct WebViewDemo: View {
var body: some View {
NavigationView {
WebView(urlString: "https://www.w3schools.com/html/tryit.asp?filename=tryhtml5_audio_all")
.navigationBarTitle("Web View")
}
}
}
struct WebView_Previews: PreviewProvider {
static var previews: some View {
WebViewDemo()
}
}
Nothing special, right?
When I build the app and navigate to a website that has an tag (https://www.w3schools.com/html/tryit.asp?filename=tryhtml5_audio_all). I still see the error when I play the audio.
It plays nonetheless, but the error is there. I'm not at all interested in actually playing audio in the background/when the app is closed/suspended. I just want the error to go away!
I've tried different iOS versions (14,15,16,17), but the problem persists.
Anyone know what's happening?
Hello All,
On our iOS app, we plan to show tutorial (WKWebview) only in case user has safari extension disabled.
How can iOS app know about status of Safari extension.
Thanks,
Anup
I am trying to setup web sign in with apple, I have an active apple subscription and have set up all necessary things. I made a service id in apple Identifiers, connected to existing primary id (also has apple sign in enabled). I have my domain set up also correctly but still I cant generate the code due to invalid client. What do I need to do?
I have also tried recreating the service ids multiple times with no luck.
my init is
AppleID.auth.init({
clientId : '[CLIENT_ID]',//used the service id one not app id
redirectURI : '[REDIRECT_URI]',
usePopup : true
});
link to generate codes now is: https://appleid.apple.com/auth/authorize?client_id=com.crmtournest.sigin&redirect_uri=https%3A%2F%2Fwww.tournestcrm.com%2Fauth%2Fcallback&response_type=code%20id_token&state=saciy7rn1km&scope=name%20email&response_mode=web_message&frame_id=03487c22-abb4-48cd-8613-d6bf5836e9eb&m=11&v=1.5.5
Also tried: https://appleid.apple.com/auth/authorize?client_id=com.crmtournest.sigin&redirect_uri=https%3A%2F%2Fwww.tournestcrm.com%2Fauth%2Fcallback&response_type=code%20id_token (not working)
I get invalid_client
setup on apple below:
We are experiencing an issue with Safari in all versions from 18.0 to 18.5 that does not occur in version 17. It affects both iPhones and Macs. And does not happen in Chrome or Windows.
The problem is impacting our customers, and our monitoring tools show a dramatic increase in error volume as more users buy/upgrade to iOS 18.
The issue relates to network connectivity that is lost randomly. I can reliably reproduce the issue online in production, as well as on my local development environment.
For example our website backoffice has a ping, that has a frequency of X seconds, or when user is doing actions like add to a cart increasing the quantity that requires backend validation with some specific frequency the issue is noticable...
To test this I ran a JS code to simulate a ping with a timer that calls a local-dev API (a probe that waits 2s to simulate "work") and delay the next HTTP requests with a dynamic value to simulate network conditions:
Note: To even make the issue more clear, I'm using GET with application/json payload to make the request not simple, and require a Pre-flight request, which doubles the issue.
(async () =&gt; {
for (let i = 0; i &lt; 30; i++) {
try {
console.log(`Request start ${i} ${new Date().toLocaleString()}`);
const res = await fetch(`https://api.redated.com:8090/1/*****/probe?`, {
method: 'GET',
mode: "cors",
//headers: {'Content-Type': 'text/plain'},
headers: { 'Content-Type': 'application/json' },
});
console.log(`Request end ${i} ${new Date().toLocaleString()} status:`, res.status);
} catch (err) {
console.error(`Request ${i} ${new Date().toLocaleString()} error:`, err);
}
let delta = Math.floor(Math.random() * 10);
console.log("wait delta",delta);
await new Promise(r =&gt; setTimeout(r, 1000 - delta));
}
})();
For simplicity lets see a case where it fails 1 time only out of 10 requests.
(Adjusting the "delta" var on the time interval create more or less errors...)
This are the results:
The network connection was lost error, which is false, since this is on my localhost machine, but this happens many times and is very reproducible in local and production online.
The dev-tools and network tab shows empty for status error, ip, connection_id etc.. its like the request is being terminated very soon.
Later I did a detailed debugging with safari and wireshark to really nail down the network flow of the problem:
I will explain what this means:
Frame 10824 – 18:52:03.939197: new connection initiated (SYN, ACK, ECE).
Frame 10831 – 18:52:04.061531: Client sends payload (preflight request) to the server.
Frame 10959 – 18:52:09.207686: Server responds with data to (preflight response) to the client.
Frame 10960 – 18:52:09.207856: Client acknowledges (ACK) receipt of the preflight response.
Frame 10961 – 18:52:09.212188: Client sends the actual request payload after preflight OK and then server replies with ACK.
Frame 11092 – 18:52:14.332951: Server sends the final payload (main request response) to the client.
Frame 11093 – 18:52:14.333093: captures the client acknowledging the final server response, which marks the successful completion of the main request.
Frame 11146 – 18:52:15.348433: [IMPORTANT] the client attempts to send another new request just one second later, which is extremely close to the keep-alive timeout of 1 second. The last message from the server was at 18:52:14.332951, meaning the connection’s keep-alive timeout is predicted to end around 18:52:15.332951 but it does not. The new request is sent at 18:52:15.348433, just microseconds after the predicted timeout. The request leaves before the client browser knows the connection is closed, but by the time it arrives at the server, the connection is already dead.
Frame 11147 – 18:52:15.356910: Shows the server finally sending the FIN,ACK to indicate the connection is closed. This happens slightly later than the predicted time, at microsecond 356910 compared to the expected 332951. The FIN,ACK corresponds to sequence 1193 from the ACK of the last data packet in frame 11093.
Conclusions:
The root cause is related to network handling issues, when the server runs in a setting of keep-alive behavior and keep-alive timeout (in this case 1s) and network timming issue with Safari reusing a closed connection without retrying. In this situation the browser should retry the request, which is what other browsers do and what Safari did before version 18, since it did not suffer from this issue.
This behaviour must differ from previous Safari versions (however i read all the public change logs and could not related the regression change).
Also is more pronounced with HTTP/1.1 connections due to how the keep-alive is handled.
When the server is configured with a short keep-alive timeout of 1 second, and requests are sent at roughly one-second intervals, such as API pings at fixed intervals or user actions like incrementing a cart quantity that trigger backend calls where the probability of failure is high.
This effect is even more apparent when the request uses a preflight with POST because it doubles the chance, although GET requests are also affected.
This was a just a test case, but in real production our monitoring tools started to detect a big increment with this network error at scale, many requests per day... which is very disrupting, because user actions are randomly being dropped when the user actions and timming happens to be just near a previous connection, where keep alive timeout kicks-in, but because the browser is not yet notified it re-uses the same connection, but by the time it arrived the server is a dead connection. The safari just does nothing about it, does not even retry, be it a pre-flight or not, it just gives this error.
Other browsers don't have this issue.
Thanks!
On iOS 26 Beta, WKWebView consistently crashes when interacting with pages that use -webkit-user-select: none.
This issue does not reproduce in Safari, but only when the same content is loaded inside a WKWebView.
Steps to Reproduce:
Install iOS 26 Beta.
Open a WKWebView that loads a webpage with the following style applied globally:
-webkit-user-select: none;
Perform the following gesture sequence inside the WKWebView:
Double tap anywhere in the web content.
On the second tap, keep your finger pressed (do not lift).
While still holding the second tap, drag your finger across the screen (pan).
This sequence reliably produces the crash.
Expected Result:
No crash. The gesture should either be ignored or handled gracefully.
Actual Result:
The app crashes 100% of the time with the following exception:
#0 0x000000013f1a0874 in __pthread_kill ()
#1 0x00000001357522ec in pthread_kill ()
#2 0x00000001801ad950 in abort ()
#3 0x00000001802fa26c in __abort_message ()
#4 0x00000001802ea1a4 in demangling_terminate_handler ()
#5 0x0000000180077218 in _objc_terminate ()
#6 0x00000001802f9758 in std::__terminate ()
#7 0x00000001802fc7c0 in __cxxabiv1::failed_throw ()
#8 0x00000001802fc7a0 in __cxa_throw ()
#9 0x000000018009c1bc in objc_exception_throw ()
#10 0x00000001804f38f8 in +[NSException raise:format:] ()
#11 0x000000018c5fb570 in -[CALayer setPosition:] ()
#12 0x0000000185d02414 in -[UIView _backing_setPosition:] ()
#13 0x00000001867ec978 in -[UIView setCenter:] ()
#14 0x0000000186666468 in -[_UIEditMenuContentPresentation _displayPreparedMenu:titleView:reason:didDismissMenu:configuration:] ()
#15 0x0000000186666088 in __54-[_UIEditMenuContentPresentation _displayMenu:reason:]_block_invoke ()
#16 0x00000001867b3ed4 in -[UIEditMenuInteraction _editMenuPresentation:preparedMenuForDisplay:completion:] ()
#17 0x0000000186665fb0 in -[_UIEditMenuContentPresentation _displayMenu:reason:] ()
#18 0x0000000186665de4 in -[_UIEditMenuContentPresentation displayMenu:configuration:] ()
#19 0x00000001867b3260 in __58-[UIEditMenuInteraction presentEditMenuWithConfiguration:]_block_invoke ()
#20 0x00000001867b4c98 in __80-[UIEditMenuInteraction _prepareMenuAtLocation:configuration:completionHandler:]_block_invoke ()
#21 0x000000018653ff80 in __109-[UITextContextMenuInteraction _editMenuInteraction:menuForConfiguration:suggestedActions:completionHandler:]_block_invoke ()
#22 0x0000000186540448 in __107-[UITextContextMenuInteraction _querySelectionCommandsForConfiguration:suggestedActions:completionHandler:]_block_invoke ()
#23 0x000000018dba84f8 in WTF::Detail::CallableWrapper<WTF::CompletionHandler<void (IPC::Connection*, IPC::Decoder*)> IPC::Connection::makeAsyncReplyCompletionHandler<Messages::WebPage::RequestDocumentEditingContext, WTF::CompletionHandler<void (WebKit::DocumentEditingContext&&)>>(WTF::CompletionHandler<void (WebKit::DocumentEditingContext&&)>&&, WTF::ThreadLikeAssertion)::'lambda'(IPC::Connection*, IPC::Decoder*), void, IPC::Connection*, IPC::Decoder*>::call ()
#24 0x000000018dca14cc in WTF::Detail::CallableWrapper<WebKit::AuxiliaryProcessProxy::sendMessage(WTF::UniqueRef<IPC::Encoder>&&, WTF::OptionSet<IPC::SendOption>, std::__1::optional<IPC::ConnectionAsyncReplyHandler>, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::$_1, void, IPC::Connection*, IPC::Decoder*>::call ()
#25 0x000000018e2d5c54 in IPC::Connection::dispatchMessage ()
#26 0x000000018e2d6118 in IPC::Connection::dispatchIncomingMessages ()
#27 0x00000001997f9c58 in WTF::RunLoop::performWork ()
#28 0x00000001997fa930 in WTF::RunLoop::performWork ()
#29 0x000000018044d4dc in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ ()
#30 0x000000018044d424 in __CFRunLoopDoSource0 ()
#31 0x000000018044cc0c in __CFRunLoopDoSources0 ()
#32 0x000000018044bd84 in __CFRunLoopRun ()
#33 0x0000000180446e24 in _CFRunLoopRunSpecificWithOptions ()
#34 0x00000001924c19bc in GSEventRunModal ()
#35 0x00000001862217a8 in -[UIApplication _run] ()
#36 0x00000001862259d0 in UIApplicationMain ()
Same issues below.
https://developer.apple.com/forums/thread/796799
https://developer.apple.com/forums/thread/796501
https://developer.apple.com/forums/thread/796874
https://developer.apple.com/forums/thread/796686
I am trying to run JavaScript only after the page has loaded, and according to here - https://developer.apple.com/documentation/safariservices/safari_app_extensions/injecting_a_script_into_a_webpage, I should use DOMContentLoaded. However, it does not seem to work.
This is my content.js file:
function runOnStart() {
document.addEventListener('DOMContentLoaded', function(e) {
document.body.style.background = "rgb(20, 20, 20)";
document.html.style.background = "rgb(20, 20, 20)";
var divElements = document.body.getElementsByTagName('div');
for(var i = 0; i < divElements.length; i++) {
let elem = divElements[i];
elem.style.background = "rgba(255, 255, 255, 0.05)";
}
});
}
runOnStart();
If I take the code outside of the event listener, it runs fine, but a lot of the elements haven't loaded in yet so it doesn't work as it should.
The function is definitely running, but the event listener simply doesn't work. I appreciate any help you can give!
(1) Context: Our project has a login feature via WEBVIEW (using SFSafariViewController) and integrates PassKey on the Web side.
The app listens for a successful login by capturing the redirect URL via the delegate of SFSafariViewController.
(2) Issue:
On iOS < 18.4: The redirect URL is captured with full parameters returned.
https://xyz.com/home?session_state=...&code=...
On iOS ≥ 18.4: The redirect URL is captured successfully but missing parameters.
https://xyz.com/home
We currently suspect that the issue originates from the SFSafariViewController framework after the release of iOS 18.4.
Has anyone experienced a similar issue?
We would also appreciate support from the Apple team.
Hello,
As previous reports have already shown, there seems to be a few issues on the latest version of Safari, mainly around:
Modals taking up the full viewport
Elements positioned at the bottom of the screen
This also seems to affect the modals on apple.com/iphone.
I've recently done an analysis of what can and can't be done in code to work with the new liquid glass UI and thought I'd share my findings here.
The full write up, along with screenshots and the demos I used are available in this repository:
https://github.com/stevenocchipinti/liquid-glass-spike
A brief summary of the findings:
The conditions for a fullscreen modal overlay element to cover the entire screen with a position: fixed; seems to be:
The background must be semi-transparent
Solid colours, linear-gradients, etc. don't work
The container must be empty
This also means the standard and ::backdrop don't seem to work.
The conditions for a bottom sheet to cover the entire screen, including the area around the Safari toolbar seems to be:
The element must be positioned within 3px from the bottom of the viewport
The height must be within a certain threshold
If I've missed anything, please let me know.
It would be really nice to have some official documentation on these issues to explain to developers how to do this properly.
We're having trouble connecting to local area network websockets in Safari in the latest iOS26 Beta 3 (iPhone 14), both secure and unsecure. Code works < iOS26 & macOS, etc.
--
Unsecure behaviour: need to call connectWebSocket() twice, establishes connection reliably. Calling connectWebSocket() once, will sometimes work, sometimes not.
--
Secure behaviour: Error in debug console, even though the certificate has been accepted and the page is loaded as https. Error:
WebSocket connection to 'wss://192.168.1.81/api/webSocket' failed: The certificate for this server is invalid. You might be connecting to a server that is pretending to be “192.168.1.81”, which could put your confidential information at risk.
--
let apiEndpoint = window.location.hostname;
if (apiEndpoint == null || apiEndpoint == '') {
apiEndpoint = "192.168.1.81";
}
function connectWebSocket() {
if (webSocket && webSocket.readyState == 1) {
return;
}
if (webSocket) {
webSocket.close();
}
webSocket = new WebSocket(
(window.location.protocol === 'https:' ? "wss://" : "ws://") + apiEndpoint + "/api/webSocket",
);
webSocket.onerror = (error) => {
console.log("WebSocket error", error);
};
webSocket.onopen = () => {
console.log("WebSocket connected");
webSocket.send("volume");
webSocket.send("isPlaying");
};
webSocket.onmessage = (event) => {
const msg = event.data;
if (!msg) return;
if (msg.startsWith("volume")) {
const volume = parseInt(msg.replace('volume:',''));
const slider = document.getElementById("volumeSlider");
slider.value = volume;
slider.style.background = `linear-gradient(to right, #007bff ${volume}%, white ${volume}%)`;
} else if (msg.startsWith("isPlaying")) {
const url = msg.replace('isPlaying:', '');
let matchedEntry = null;
let coverToSelect = null;
categories.forEach((category, catIdx) => {
category.entries.forEach((entry, entryIdx) => {
if (entry.url === url) {
matchedEntry = entry;
coverToSelect = document.querySelector(`.cover[category-idx="${catIdx}"][entry-idx="${entryIdx}"]`);
}
});
});
if (matchedEntry && coverToSelect) {
selectCover(coverToSelect, true);
showNowPlayingBar(matchedEntry);
const top = coverToSelect.getBoundingClientRect().top + window.scrollY - 150;
window.scrollTo({ top, behavior: 'smooth' });
}
}
};
webSocket.onclose = () => {
console.log("WebSocket closed, retrying...");
setTimeout(connectWebSocket, 1000);
};
}
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "visible") {
connectWebSocket();
}
});
connectWebSocket();
When configuring a Per-App VPN payload with 250+ Safari domains under a managed app, the VPN does not trigger for the other managed apps(like chrome etc). However, the same VPN successfully starts and works when used with Safari. Reducing the number of Safari domains in the VPN payload resolves the issue, allowing the VPN to trigger for the managed app as expected.
Has anyone else faced this issue, and what's the workaround for it?
I am posting here because we have an urgent issue affecting the operation of our service and are in need of a solution after our own analysis has come up with few answers.
Beginning in iOS 18.2.x, we experienced exactly the same issue as the author of this thread, as we are also operating a service that allows for device certificate login for users configured to require one:
https://developer.apple.com/forums/thread/767374
The author seems to have resolved the issue but the fix mentioned in the thread did not resolve our problem for iOS devices with iOS 18.2.x installed and the contents of that private support ticket are, of course, not visible to us. Furthermore, we have a different issue that surfaced with the release of iOS 18.3.x.
Namely, the issue in iOS 18.3.x is more severe than the one in iOS 18.2.x, in that instead of simply taking a long time for the certificate/identity selection dialog to appear, it simply fails immediately and is returning a “no certificate selected” response to our server.
One thing to note here is that, curiously, if we wait for several seconds (about 10-15 seconds) this behavior is not replicated. So, it seems there is potentially something going in the background, and the certificate selection process will only occur successfully like before if we wait. This is a very unideal workaround.
After entering user credentials, we have the user navigate to a dedicated certificate authentication page. On the BIG IP side, upon users visiting this page, we have it configured to apply an SSL profile that contains appropriate CAs for the given user, and then requests to the browser that a new connection requiring a certificate be made.
We are investigating this by checking logs in in a variety of places:
We can verify in BigIP logs that a response is being returned to the server without a certificate included. For the sake of our application, this is handled as a “user did not select a certificate” event, and thus the attempted login is failed. Using the MacOS “Console” application, we are able to see the following logs from the “trustd” process of the target iOS 18.3.x device:
Failure case:
debug 11:19:49.648581+0900 trustd XPC [com.apple.WebKit[1034]/1#25 LF=0] operation: trust_evaluate (8)
debug 11:19:49.648766+0900 trustd complex trust settings anchor
Successful case (after waiting 10-15 seconds after initial login page load/before moving to certificate page):
debug 11:26:02.803153+0900 trustd XPC [MobileSafari[1031]/1#169 LF=0] operation: trust_evaluate (8)
debug 11:26:02.804219+0900 trustd non ev score: 121 <private>
There appears to be no attempt by MobileSafari to initiate the display of a certificate selection window in the failure cases. The iOS device is swift to return a response with no certificate selected to Big IP, and the result of “no certificate selected” is thus propagated through Big IP and ultimately to our web service.
Does anyone have any advice or information on the following?
Recommended tools to gather more data that may be pertinent.
Any ideas on changes in iOS 18.2.x+ that could have resulted in the behavior changing as described above?
If more information is necessary, I will do my best to supply it. Thank you in advance!
Topic:
Safari & Web
SubTopic:
General
Subject: Apple Pay JS - "Payment Was Cancelled by the User" Issue (Braintree + Server-Side Validation)
Issue
I am implementing Apple Pay using Braintree with server-side validation. However, when initiating the payment process, I receive the following error:
[Log] Payment was cancelled by the user: (apple-pay-test-ucxp.onrender.com, line 286)
Additionally, the console logs an ApplePayCancelEvent with:
sessionError: {code: "unknown", info: {}}
Despite successfully fetching merchant session validation data from the backend and completing merchant validation, the payment process does not proceed.
Setup Details
Payment Processor: Braintree (Apple Pay integration)
Backend API: Fetching merchant session validation via createPaymentSessionGet
Payment Processing: Using Braintree nonce tokenization
Client-Side Code (Key Sections)
session.onvalidatemerchant = async (event) => {
try {
const merchantSession = await fetch(
"https://api.paybito.com:9443/ApplePay/api/apple-pay/createPaymentSessionGet",
{
method: "GET",
headers: { "Content-Type": "application/json" },
}
).then((res) => res.json());
session.completeMerchantValidation(merchantSession);
} catch (err) {
console.error("Merchant validation failed:", err);
session.abort();
}
};
session.onpaymentauthorized = async (event) => {
try {
const payload = await applePayInstance.tokenize({
token: event.payment.token,
});
const response = await fetch(
"https://api.paybito.com:9443/ApplePay/api/braintree/process-payment",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ nonce: payload.nonce }),
}
).then((res) => res.json());
if (response.success) {
session.completePayment(ApplePaySession.STATUS_SUCCESS);
} else {
session.completePayment(ApplePaySession.STATUS_FAILURE);
}
} catch (err) {
console.error("Payment authorization failed:", err);
session.completePayment(ApplePaySession.STATUS_FAILURE);
}
};
Observations
Merchant validation completes successfully.
The error occurs before onpaymentauthorized executes.
Session error code is unknown, making debugging difficult.
Questions
Has anyone encountered this issue before?
Could this be related to how the validation session is fetched from the backend?
Is there a way to obtain more meaningful debug information from Apple Pay?
Any insights would be greatly appreciated!
Hi everyone,
We’ve recently run into an issue with Apple Pay on the web and would appreciate some clarification.
Background:
Previously, we integrated Apple Pay without using the Apple Pay JS SDK.
We relied on ApplePaySession.canMakePayments() to check availability, and it worked fine.
After Apple announced support for browsers beyond Safari, we switched to the Apple Pay JS SDK.
According to Apple’s documentation, we should now use applePayCapabilities() for capability checks in third-party browsers.
Our current behavior:
We implemented applePayCapabilities().
Initially, it was returning either paymentCredentialStatusUnknown or paymentCredentialsUnavailable.
Based on those values, we displayed the Apple Pay button.
The problem:
About a week ago, on the same device/browser, applePayCapabilities() started returning applePayUnsupported.
Setup: MacBook Pro 13-inch (M1, 2020), Google Chrome Version 136.0.7103.93.
The Apple documentation says: “Don’t show an Apple Pay button or offer Apple Pay” when the result is applePayUnsupported.
However, at the same time, canMakePayments() is returning true.
This creates a direct conflict between the two recommendations:
canMakePayments() → true ⇒ show the button.
applePayCapabilities() → applePayUnsupported ⇒ don’t show the button.
Question:
What’s the correct approach here?
Should we prioritize applePayCapabilities() and hide the button, or is it acceptable to continue relying only on canMakePayments() as the source of truth for showing Apple Pay?
Any insights from others who’ve run into this contradiction would be very helpful.
Thanks in advance!