Hi!
My SwiftUI app is a rather complex browser app. Starting with iOS 18, the app crashes due to repeted reloads of the WkWebView. I’ve tracked the issue as far as I can, but I still haven’t found the root cause.
My app is structured like this:
MainView holds a cuple of subviews. It also holds a @StateObject called viewModel that holds a lot of @Published vars. The viewModel is passed as a enivormentObject.
Example from ViewModel:
@MainActor class ViewModel: NSObject, ObservableObject {
@Published public var isLoading: Bool = false
@Published public var loadProgress: Double? = 0
public func setIsLoading(_ value: Bool) async {
self.isLoading = value
}
public func setLoadProgress(_ value: Double?) async {
self.loadProgress = value
}
}
WebView is a subview of MainView, which holds a navigation bar, and a UIViewRepresentable, which is a WkWebView.
The WkWebView pushes some states to the ViewModel as the underlying values of the WkWebView changes, i.e. estimaedProgress, and isLoading. This is done via KVO and works like this:
estimatedProgressObservation = self.parent.webView.observe(\.estimatedProgress) { webView, progress in
Task {
await parent.viewModel.setLoadProgress(webView.estimatedProgress)
}
}
isLoadingObservation = self.parent.webView.observe(\.isLoading) { webView, value in
Task {
await parent.viewModel.setIsLoading(webView.isLoading)
}
}
By using a timer in WkWebViews Coordinator, i trigger a load after a configurable amount of time :
func loadUrl(url: URL) {
DispatchQueue.main.async {
console.info("Load URL: ...", sensitive: "Load URL: \(url.absoluteString)")
let policy: NSURLRequest.CachePolicy
if self.parent.settings.noCache {
policy = .reloadIgnoringLocalAndRemoteCacheData
} else {
policy = .useProtocolCachePolicy
}
let request = URLRequest(url: url, cachePolicy: policy)
self.parent.webView.load(request)
}
}
Running the app with the automatic reload enabled freezes the app after a couple of hours. It also seems to freeze Safari on the device. The device needs to be rebooted.
If I inspect the device's running processes, hundreds of ”com.apple.webkit. web content " processes are running.
Removing await parent.viewModel.setLoadProgress(webView.estimatedProgress) and await parent.viewModel.setIsLoading(webView.isLoading) fixes the issue, but it is necessary for other app functions. Therefore, is suspect that the viewModel somehow causes the bug.
The issue arises after a couple of loads 5-10. The debugger shows a message when the processes start to pile up. I suspect its related.
Failed to terminate process: Error Domain=com.apple.extensionKit.errorDomain Code=18 "(null)" UserInfo={NSUnderlyingError=0x12d0e7f60 {Error Domain=RBSServiceErrorDomain Code=1 "Client not entitled" UserInfo={RBSEntitlement=com.apple.runningboard.terminateprocess, NSLocalizedFailureReason=Client not entitled, RBSPermanent=true}}}
How can I find out what causes the suspected memory leak? Instruments gives me nothing of value. The memory leak wasn't present in iOS 17. Is this a bug in iOS 18, or was something intentionally changed?
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hi!
I have a rather complicated SwiftUI browser app with a WKWebView. There is an option to reload the website after a configurable amount of time. Starting with iOS 18, the app crashes after repeated reloads. How many reloads that are required depends on the device, sometimes 100, sometimes 1000.
Reloading is done via a timer that triggers the following code on the main thread:
let request = URLRequest(url: url, cachePolicy: policy)
self.parent.webView.load(request)
The URL is configurable and cachePolicy can be either .reloadIgnoringLocalAndRemoteCacheData or .useProtocolCachePolicy
How the crash affects the device also differs from device to device and from time to time. I have suffered from the following crashtypes:
App is killed
App is killed and Safari also stops working
App is killed and the whole OS is really slow
The WKWebView stops loading and hangs at 20%.
The device is rebooted
My app has an option to disable cache. Cache is disabled by setting cachePolicy to .reloadIgnoringLocalAndRemoteCacheData and by removing all cache in a rather complicated way.
Basicly i'm doing something like this:
dataStore.removeData(ofTypes: types, modifiedSince: Date.distantPast, completionHandler: nil)
if let klazz = NSClassFromString("Web" + "History"),
let clazz = klazz as AnyObject as? NSObjectProtocol {
if clazz.responds(to: Selector(("optional" + "Shared" + "History"))) {
if let webHistory = clazz.perform(Selector(("optional" + "Shared" + "History"))) {
let o = webHistory.takeUnretainedValue()
_ = o.perform(Selector(("remove" + "All" + "Items")))
}
}
}
if let cachesPath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first {
let contents = (try? FileManager.default.contentsOfDirectory(atPath: cachesPath)) ?? []
for file in contents {
if foldersToDelete.contains(file) {
let path = cachesPath.appending("/").appending(file)
do {
try FileManager.default.removeItem(atPath: path)
} catch {
print("Can't delete cache file: \(path), error: \(error.localizedDescription)")
}
}
}
}
The cache state affects the intensity of the crash. Disabling the cache shortens the time the app is working, while enabling the cache reduces the intensity of the bug.
Based on my investigation, I suspect that loading a website in a WKWebVew leaks memory in iOS 18. If the whole website needs to be requested (= caching off), it results in a more significant memory leak and a faster crash time.
Is this a known issue? Am I doing something wrong? Is there a potential workaround?