After updating Xcode to the latest version we observed that SFSafariViewController is not loading web pages on Xcode 16 Simulator with iOS 18, whenever it is presented the View Controller is empty (does not load any content) and the app freezes, but other screens that use WKWebView are working normally.
Also, during tests on physical devices with iOS 18 it seems to work just fine, so it might be just a IDE version problem.
Is anyone experiencing the same issue?
If so, did anyone find a solution for it?
Safari Services
RSS for tagEnable web views and services in your app using Safari Services.
Posts under Safari Services tag
41 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Hi, I am having issue with WebAssembly not able to load wasm file on Safari web extension.
It is showing CompileError: Refused to create a WebAssembly object because 'unsafe-eval' or 'wasm-unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'wasm-unsafe-eval'".
It was working fine 2 month ago, my original CPS is "script-src 'self' 'unsafe-eval'". But now it is not accepting 'unsafe-eval', I also tried 'wasm-unsafe-eval' did not work.
Is there any changes on Safari browser regarding the CSP for WebAssenbly? Please let me know what CPS value will work.
Here is the example code on how I load the WebAssembly wasm file.
fetch('test_wasm_lib.wasm')
.then(response => {
if (!response.ok) throw new Error('Network response was not ok');
return response.arrayBuffer();
})
.then(bytes => WebAssembly.instantiate(bytes))
.then(results => {
// Use your WebAssembly instance here
console.log('load wasm success')
})
.catch(error => {
console.error('Error loading WASM:', error);
});
Is it possible to enable/disable the enabled flag before the extension is loaded? we want to have a button in our app which controls the availability of the content blocker ruleSet in declarative_net_request in manifest (version 2).
"declarative_net_request": {
"rule_resources": [
{
"id": "ruleset_1",
"enabled": true,
"path": "ruleset_1.json"
}
]
},
We are using manifest version 2, and currently some dynamic ads which come under the #document (documentURL) are not getting fetched and we are not able to block.
is there an alternative for onBeginRequest in iOS Safari? How can we fetch the dynamic URLs otherwise?
We have observed that blocking content using Safari web extension does not fetch few URLS within the #document (documentURLs) because the onBeforeRequest webextension API is currently not available in Safari iOS.
But it works fine using the Content blocking extension.
We have a list of URLs which we want to block from the website. Which extension would you suggest the Content blocking extension or the Safari web extension?
Hi everyone,
I’m working on an iOS app using WKWebView, and I have a specific use case involving cross-origin iframes and form autofill. I’m wondering if it’s possible to programmatically fill input elements, such as credit card numbers, within a cross-origin iframe loaded in a WKWebView.
I understand that due to the Same-Origin Policy, direct DOM manipulation of cross-origin iframes is restricted. However, I’m curious if there are any methods or workarounds that might allow me to achieve this, specifically within the context of WKWebView. Thanks.
We have an iOS Safari extension currently distributed via Testflight.
I’ve noticed that after an indeterminate period of time (sometimes days, sometimes weeks) our safari extension will stop working. It will need to be turned on again from the system general -> safari -> extensions menu.
This is occurring on both iPhones and iPads running 17.6.1.
Is there any condition that will cause the system to disable a safari extension, requiring the user to reopen iOS settings to re-enable?
We have an iOS Safari extension currently distributed via Testflight.
I’ve noticed that after an indeterminate period of time (sometimes days, sometimes weeks) our safari extension will stop working and will need to be turned on again from the system general -> safari -> extensions menu.
This is occurring on both iPhones and iPads running 17.6.1.
Is there any condition that will cause the system to disable a safari extension, requiring the user to reopen iOS settings to re-enable?
We have implemented a content blocker using the Safari Web Extension, which can be toggled on or off as needed. However, we've noticed that changes in the Safari browser take effect only after the page is reloaded again. This behaviour has been observed across all simulators as well as on iPhone 8 Plus running iOS 16.7.8.
Is this due to a delay in the JS file rules updation on simulator and lower devices?
Hi everyone, when My project (Angular 5) running in Safari Browser, we have a download function, it's download the excel file, end with .xlsx, but when running in low version Safari version, it will have a popup, that is not my project did, it blocked be safari itself, you can check the image, my question is how to hide the 'View' button or directly download instead of show the popup?
We have implemented content blocking using Safari web extension, it blocks the content but after reloading the current page about 3-4 times the blocked content re-appears, how can we fix this issue?
In Safari, when there are two password fields side-by-side, they are actually asking for different passwords. However, the browser's autofill might automatically fill the second field with the same password as the first, assuming it's a confirmation.
I'm attempting to reload a Safari Content Blocker from within a sandboxed command-line tool configured as a LaunchAgent. However, when I use SFContentBlockerManager to reload the content blocker, I encounter the error SFErrorDomain Code=1: Unavailable error.
Is it possible to reload a content blocker from a LaunchAgent? If so, how can it be done?
//
// main.swift
// BlockerUpdater
//
// Created by Sebastian Livoni on 30/06/2024.
//
import Foundation
import SafariServices
// Function to reload content blocker asynchronously
func reloadContentBlocker() async {
NSLog("Hello, World!")
do {
try await SFContentBlockerManager.reloadContentBlocker(withIdentifier: "me.livoni.blocker.dns")
NSLog("Reload complete")
} catch {
NSLog("Failed to reload content blocker: \(error.localizedDescription)")
}
}
// Main entry point for async code
@main
struct BlockerUpdater {
static func main() async {
await reloadContentBlocker()
}
}
Hello,
I've encoutered an issue with Safari App Extensions. My extension prints lots of suspect error logs in the Xcode console and inside Console.app. This happens basically whenever I make any interaction with the App Extension or with Safari. The most common and predictable error log I get is:
No current extension context; trying most recent context
(Subsystem: com.apple.SafariServices)
However, I also sometimes get the following error messages, albeit less frequently, which may be related:
No extension context for best match
No extension context for remote object
Error connecting back to host for remote object: NSCocoaErrorDomain, code: 4099
No known extension contexts for profile 00000000-0000-0000-0000-000000000000
Most recent extension context B7223E12-B563-45E0-97F8-50500BC6B994 does not have connection back to host; trying best match context
I haven't been able to find anything about these error logs in Apple documentation or on the Internet, so I did a bit of empirical investigation.
I reproduced the bug in the following basic scenario: I've created a new Safari App Extension project in Xcode by going to File > New > Project > Safari Extension App. I've selected "Safari App extension" for the type and "Swift" for the language. The project comes by default with a "SafariExtensionHandler.swift" file, which includes the following code:
override func validateToolbarItem(in window: SFSafariWindow, validationHandler: @escaping ((Bool, String) -> Void)) {
validationHandler(true, "")
}
No issues so far.
If I add the following call:
override func validateToolbarItem(in window: SFSafariWindow, validationHandler: @escaping ((Bool, String) -> Void)) {
validationHandler(true, "")
SFSafariApplication.getActiveWindow { window in
// code
}
}
There are still no error messages logged in the Console.
However, if I do this instead:
override func validateToolbarItem(in window: SFSafariWindow, validationHandler: @escaping ((Bool, String) -> Void)) {
validationHandler(true, "")
DispatchQueue.main.async {
SFSafariApplication.getActiveWindow { window in
// code
}
}
}
Then my Xcode console starts being spammed with "No current extension context; trying most recent context" error logs.
With some more testing, it seems that the most common/predictable situation that causes the error log seems to be when calling any Safari API (e.g. SFSafariApplication.getActiveWindow{} or even SFSafariApplication.setToolbarItemsNeedUpdate()) outside of a direct method call provided by the Safari App Extension API. So making API calls directly from inside validateToolbarItem(in:, validationHandler:) or messageReceived(withName:from:userInfo:) calls is fine, but anything else causes "No extension context" logs. The bug even reproduces if you make a Safari API call directly inside of an @IBAction method call caused by a button click inside the Safari popover of the Safari App Extension.
With this being the case, it seems to be impossible to make clean Safari API calls in an asynchronous or proactive way, which is problematic for our app extension use case and which seems to defeat the purpose of some of the API calls like SFSafariApplication.setToolbarItemsNeedUpdate(). Also, this seems to be a new issue.
I've tested these scenarios on various macOS versions that I had on hand (specifically, on macOS 10.15 Catalina, macOS 13 Ventura and macOS 14 Sonoma) and the bug seems to reproduce only on macOS 14 Sonoma. The Safari App Extension behaves as expected on previous macOS versions, with no suspect error logs.
Does anyone know what this issue is about?
This method does not seem to work consistently: SFSafariViewControllerDelegate.safariViewController(_ controller: SFSafariViewController, initialLoadDidRedirectTo URL: URL)
It was working 100% of the time until this week for me, now it has about a 1-5% success rate for detecting redirects.
The docs are not clear:
@discussion This method may be called even after -safariViewController:didCompleteInitialLoad: if
the web page performs additional redirects without user interaction.
Code snippet from calling UIViewController that conforms to SFSafariViewControllerDelegate:
let config = SafariViewController.Configuration()
config.entersReaderIfAvailable = false
let vc = SFSafariViewController(url: url, configuration: config)
vc.delegate = self
self.safariViewController = vc
print(self.safariViewController?.delegate)
self.present(vc, animated: true)
Did something change? How can I make this consistent again?
For some reason, not all of the time but most of the time, the SFSafariViewControllerDelegate method for initialLoadDidRedirectTo is not being triggered for subsequent reloads as specified in the docs.
@discussion This method may be called even after -safariViewController:didCompleteInitialLoad: if
the web page performs additional redirects without user interaction.
I am allowing a user to log in with an OAuth 2.0 Provider on the Safari browser and expecting to detect the redirect to continue the flow from the app once their credentials have securely been consumed by the IdP in Safari.
It was working consistently. It went from a 100% success rate up until this week to maybe 1/20 successful redirects.
Code snippet:
let config = SafariViewController.Configuration()
config.entersReaderIfAvailable = false
let vc = SFSafariViewController(url: url, configuration: config)
vc.delegate = self
self.safariViewController = vc
print(self.safariViewController?.delegate)
self.present(vc, animated: true)
Why is it not always detecting the redirects?
Preamble: I am creating an iOS build of a Ren'Py game. My coding experience with Swift/ObjC is nearly nonexistent and I've primarily followed tutorials up to this point. Ren'Py uses an underlying framework to create an Xcode project. I do not have control over how this framework does things, but I can add files before actually compiling inside Xcode. I MUST use the pyobjus library to do so based on my current understanding, abilities, and frameworks. The included IAPHelper class processes in-app purchasing and it works great! The following function indicates, however, that there is a rootViewController that I need to attach to.
- (void) showDialog {
if (alert != nil) {
return;
}
alert = [UIAlertController
alertControllerWithTitle:self.dialogTitle
message:nil
preferredStyle:UIAlertControllerStyleAlert ];
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleLarge];
// Adjust the indicator so it is up a few pixels from the bottom of the alert
indicator.center = CGPointMake(alert.view.bounds.size.width / 2, alert.view.bounds.size.height - 50);
[indicator startAnimating];
[alert.view addSubview: indicator];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
[UIApplication.sharedApplication.keyWindow.rootViewController presentViewController:alert animated:YES completion:nil];
#pragma clang diagnostic pop
}
Problem: I am TRYING to implement an in-app browser for Patreon authentication purposes. The files I have put together are as follows:
BrowserViewController.h
// BrowserViewController.h
#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>
@interface BrowserViewController : UIViewController
- (void)loadURL:(NSString *)urlString;
@end
BrowserViewController.m
// BrowserViewController.m
#import "BrowserViewController.h"
@interface BrowserViewController ()
@property (nonatomic, strong) WKWebView *webView;
@end
@implementation BrowserViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame];
[self.view addSubview:self.webView];
NSLog(@"viewDidLoad");
}
- (void)loadURL:(NSString *)urlString {
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
NSLog(@"loadURL");
}
@end
OAuth2Strategy.py
init python in auth:
from urllib.parse import urlencode, urlparse, parse_qs
from urllib.request import urlopen, Request
import ssl
import certifi
from store import webserver, OpenURL
import json
class OAuth2Strategy():
def __init__(self, authorization_url, token_url, client_id, client_secret, callback_url, scope):
self.authorization_url = authorization_url
self.token_url = token_url
self.client_id = client_id
self.client_secret = client_secret
self.callback_url = callback_url
self.scope = scope
self.on_success_callback = None
self.on_fail_callback = None
def run(self, on_success_callback = None, on_fail_callback = None):
self.on_success_callback = on_success_callback
self.on_fail_callback = on_fail_callback
webserver.start(self)
if renpy.renpy.ios:
from pyobjus import autoclass, objc_str
# Import the Objective-C class
BrowserViewController = autoclass('BrowserViewController')
# Create an instance of the BrowserViewController
browser = BrowserViewController.alloc().init()
# Load a URL
url = self.make_authorize_url()
browser.loadURL_(objc_str(url))
elif renpy.renpy.android:
pass
else:
renpy.run(OpenURL(self.make_authorize_url()))
def make_authorize_url(self):
return self.authorization_url + "?client_id={client_id}&scope={scope}&redirect_uri={redirect_uri}&response_type=code".format(
client_id=self.client_id,
scope=self.scope,
redirect_uri=self.redirect_uri,
)
@property
def redirect_uri(self):
return "http://127.0.0.1:" + str(webserver.PORT) + self.callback_url
def handle_auth(self, request):
parsed_path = urlparse(request.path)
query = parse_qs(parsed_path.query)
code = query.get("code")
if not code:
request.send_response(400)
request.send_header('Content-type', 'text/html')
request.end_headers()
request.wfile.write(b'Failed to authenticate. You can now close this window.')
webserver.stop()
if self.on_fail_callback:
self.on_fail_callback()
return
code = code[0]
tokens = self.get_tokens(code)
request.send_response(200)
request.send_header('Content-type', 'text/html')
request.end_headers()
request.wfile.write(b'Success! You can now close this window.')
webserver.stop()
if self.on_success_callback:
self.on_success_callback(tokens)
def get_tokens(self, code):
ctx = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH, cafile=certifi.where())
data = urlencode({
"grant_type": "authorization_code",
"code": code,
"client_id": self.client_id,
"client_secret": self.client_secret,
"redirect_uri": self.redirect_uri,
}).encode("utf-8")
headers = {"Content-Type": "application/x-www-form-urlencoded"}
req = Request(self.token_url, data=data, headers=headers, method="POST")
response = urlopen(req, context=ctx)
data = response.read().decode("utf-8")
data = json.loads(data)
return data
If I use the default OpenURL function Safari opens as the full blown browser rather than in-app, hence why I am trying to override it. When I run my app and click the button that SHOULD spawn the browser, nothing happens. I can see that my URL is getting pushed to the function in the log along with Warning: -[BETextInput attributedMarkedText] is unimplemented and Failed to request allowed query parameters from WebPrivacy. though my admittedly light research indicates this isn't an issue. I have a feeling I'm not attaching my webView to the right hierarchy but I'm not sure exactly what I'm doing wrong.
I have a link that is embedded in the app using WKWebView. This link is working fine in the Macbook Safari browser, But it's not open in iPhone Safari and wkWebView in the app.
Hi, I am developing a drawing app using SafariView and is using apple pencil double tap handler for crucial features.
In IOS 17.5 I lost functionality of it when interacting with SafariView, my friend confirmed that it worked with IOS 17.4 and I can confirm it also worked with IOS 17.1. However, I cannot downgrade my system nor can my uses. THIS IS A FATAL MALFUNCTION FOR MY APP.
Detailedly, as I tested, the double tap handlers cannot be activated when my last tapped component is SafariView, so it never works when interacting with my web app. But I can enable this by clicking outside the SafariView or some non-SafariView component on top of it, just anything other than the Safari view, even webkit view can work but it is not usable for me. My aim is to keep listening to double tapping while interacting with SafariView in full screen, so I cannot let the user tap elsewhere to just activate double tap, there is no other walk around for me, unless there is a way to enable 120fps animation in WebKit view, which is only available in safari feature flags as I know.
I would like to hear a solution in this situation or a promise of a fix, this is devastating to my users' experience.
code to reproduce:
import SwiftUI
import WebKit
import SafariServices
struct SafariView: UIViewControllerRepresentable {
let url: URL
func makeUIViewController(context: Context) -> SFSafariViewController {
return SFSafariViewController(url: url)
}
func updateUIViewController(_ uiViewController: SFSafariViewController, context: Context) {
// No update code needed for this example
}
}
struct WKWebViewWrapper: UIViewRepresentable {
let url: URL
func makeUIView(context: Context) -> WKWebView {
let webView = WKWebView()
let request = URLRequest(url: url)
webView.load(request)
return webView
}
func updateUIView(_ uiView: WKWebView, context: Context) {
// No update code needed for this example
}
}
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
.onPencilDoubleTap { value in
print("tap1")
}
Text("Hello, world!")
.padding()
SafariView(url: URL(string: "https://www.example.com")!)
.onPencilDoubleTap { value in print("tap2")}
.frame(width:300)
WKWebViewWrapper(url: URL(string: "https://www.example.com")!).onPencilDoubleTap { value in
print("tap3")
}
.frame(width:300)
}
}
}
#Preview {
ContentView()
}
I want to implement in-app purchases for my Mac Safari web extension.
I can think of two ways:
Draw the payment UI in an extension web page, and send a message to the native extension app to call StoreKit code.
Open the container app from an extension web page, where the app draws the payment UI.
I couldn't make #1 work with either
StoreKit 2, which is async, and context.completeRequest(returningItems:) doesn't want to be called in a Task, saying context is not sendable)
or StoreKit 1, where calling context.completeRequest(returningItems:) in paymentQueue(_:updatedTransactions:) for some reason doesn't return a response to the extension's web page.
I couldn't make #2 work because I couldn't find a way to open the container app from the web extension. I registered a custom URL for my container app, but context.open that url does nothing.
Web extensions that use IAP with #2 are available on the Mac app store, so it must be possible, could anyone shed some light on how to open the container app and pass the purchased info to the extension web page even if the container app is not open?
Thanks in advance.