WebKit binary compatibility broken in iOS 18

The non-async signature for WKNavigationDelegate's decidePolicyForNavigationAction has changed between iOS 17 and 18.

In 17 it is func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void)

In 18 it adds the MainActor attribute to the decision handler func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping @MainActor (WKNavigationActionPolicy) -> Void)

We deliver xcframeworks to our customers with objects that implement this function, and when those customers upgraded to Xcode 16.0 / iOS 18, those frameworks broke despite ABI stability (since that's unrelated to the WebKit SDK's public signatures). I presume this is because the WebKit dependency in the iOS 18 SDK does not match the signature used those older frameworks (and the IDE errors seem to support this).

We will of course deliver new frameworks targeting the new SDK, but that takes regression testing and release planning. In the meantime, this has been exacerbated by the fact that macOS Sequoia does not support Xcode 15. Those of our customers whose machines updated to Sequoia cannot build their projects at all until we're able to deliver frameworks that are compatible.

Was this an expected change? I didn't see any deprecation warnings in iOS 17, and unless we're supposed to bundle WebKit within the framework somehow, I'm not sure how we could have done this without forcing the Xcode versioning on our customers.

Is there a way we package this differently or some other solution to make our existing frameworks compatible with iOS 18 without having to target that SDK?

To give us a common starting point for understanding your question, could you open a bug report using Feedback Assistant, and attach a test project using your XCFramework in a way that shows the build errors and what you think is the potential ABI break? That report with the test project will be very helpful to facilitate conversations around your questions. Once filed, please post the FB number here so I can locate it.

—Ed Ford,  DTS Engineer

Thank you for your time. You can see a sample in https://feedbackassistant.apple.com/feedback/15210301

Note that I'm pretty sure that objective-c must be in play here, along with probably some combination of swift/objc bridging and subclasses.

[quote='804849022, dhunts, /thread/764176?answerId=804849022#804849022, /profile/dhunts']

I'm pretty sure that objective-c must be in play here

[/quote]

To some extant, yes. As part of Swift 6 work, methods in the SDK have received annotations designating what's sendable or bound to the main actor. So, for WebKit, the method in question here was declared like this in the iOS 17.5 SDK (in WKNavigationDelegate.h):

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler WK_SWIFT_ASYNC(3);

and like this in the iOS 18 SDK:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(WK_SWIFT_UI_ACTOR void (^)(WKNavigationActionPolicy))decisionHandler WK_SWIFT_ASYNC(3);

That extra WK_SWIFT_UI_ACTOR macro on the declaration produces the @MainActor annotation when expressed through the Swift interface:

optional func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping @MainActor @Sendable (WKNavigationActionPolicy) -> Void)

Since your XCFramework exposes this method as part of the framework's public interface, when a client of your framework builds with the iOS 18 SDK, they get a build error because your definition of this overridden function from the iOS 17 SDK does not match the definition in the iOS 18 SDK that the complier is comparing it against. This is a source breaking change rather than ABI breaking because your client is recompiling using the new SDK with the updated WebKit interface, even though your XCFramework is linked with the older SDK.

We recognize that the build errors that fall out of this circumstance is problematic in situations like yours, and we're investigating ways to make this more pleasant. (Thanks for that bug report!)

On your end, as the XCFramework developer, your long-term view here should be that the updated function signature is the correct one for future iterations of your XCFramework where you ship the XCFramework from Xcode 16, as this will play well with Swift 6, since that's the reason the WebKit interface was decorated with the new attributes. For the short term, as an XCFramework developer, you always need to be compiling for the oldest Xcode version that clients of your XCFramework are using, so you can stick with compiling your framework with Xcode 15 (and the iOS 17 SDK), though any of your clients will need to also remain with Xcode 15 for the moment as well as your investigation that I mentioned above proceeds. If you happen to be in a circumstance where you can easily require all clients of your XCFramework use Xcode 16 (such as other teams within your company), you can recompile your XCFramework with Xcode 16 using the modified function signature from the iOS SDK.

— Ed Ford,  DTS Engineer

WebKit binary compatibility broken in iOS 18
 
 
Q