Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ struct OSIABWebViewConfigurationModel {
configuration.ignoresViewportScaleLimits = ignoresViewportScaleLimits
configuration.allowsInlineMediaPlayback = allowsInlineMediaPlayback
configuration.suppressesIncrementalRendering = suppressesIncrementalRendering
let windowOpenScript = WKUserScript(
source: """
window.open = function(url, target, features) {
if (url) { window.location.href = url; }
};
""",
injectionTime: .atDocumentStart,
forMainFrameOnly: false
)
configuration.userContentController.addUserScript(windowOpenScript)
return configuration
}
}
78 changes: 76 additions & 2 deletions Sources/OSInAppBrowserLib/WebView/OSIABWebViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ class OSIABWebViewModel: NSObject, ObservableObject {

/// Indicates if first load is already done. This is important in order to trigger the `browserPageLoad` event.
private var firstLoadDone: Bool = false
/// Indicates if a download is in progress, to suppress navigation errors caused by download redirects.
private var isDownloadInProgress: Bool = false
/// Retains the active WKDownload to prevent it from being deallocated mid-download.
private var activeDownload: AnyObject?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way these variables are declared suggests that only one download can be in progress at a time. Is it guaranteed that multiple simultaneous downloads can never occur?

/// Stores the destination URL for the active download.
private var downloadDestinationURL: URL?

/// Custom headers to be used by the WebView.
private let customHeaders: [String: String]?
Expand Down Expand Up @@ -206,6 +212,18 @@ extension OSIABWebViewModel: WKNavigationDelegate {
}
}

@available(iOS 14.5, *)
func webView(_ webView: WKWebView, navigationAction: WKNavigationAction, didBecome download: WKDownload) {
download.delegate = self
}

@available(iOS 14.5, *)
func webView(_ webView: WKWebView, navigationResponse: WKNavigationResponse, didBecome download: WKDownload) {
isDownloadInProgress = true
activeDownload = download
download.delegate = self
}

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
if !firstLoadDone {
callbackHandler.onBrowserPageLoad()
Expand All @@ -225,13 +243,69 @@ extension OSIABWebViewModel: WKNavigationDelegate {
}

private func handleWebViewNavigationError(_ delegateName: String, error: Error) {
print("webView: \(delegateName) - \(error.localizedDescription)")
if (error as NSError).code != NSURLErrorCancelled {
let nsError = error as NSError
if isDownloadInProgress && nsError.code == 102 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we replace the magic number 102 with a named constant or add a comment explaining its purpose?

isDownloadInProgress = false
return
}
if nsError.code != NSURLErrorCancelled {
self.error = error
}
}
}

// MARK: - WKNavigationDelegate (response) implementation
extension OSIABWebViewModel {
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
if navigationResponse.canShowMIMEType {
decisionHandler(.allow)
} else {
if #available(iOS 14.5, *) {
decisionHandler(.download)
} else {
decisionHandler(.cancel)
}
}
}
}

// MARK: - WKDownloadDelegate implementation
@available(iOS 14.5, *)
extension OSIABWebViewModel: WKDownloadDelegate {

func download(_ download: WKDownload, decideDestinationUsing response: URLResponse, suggestedFilename: String, completionHandler: @escaping (URL?) -> Void) {
let tempDir = FileManager.default.temporaryDirectory
let fileURL = tempDir.appendingPathComponent(suggestedFilename)
if FileManager.default.fileExists(atPath: fileURL.path) {
try? FileManager.default.removeItem(at: fileURL)
}
downloadDestinationURL = fileURL

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we clean up the downloaded files at some point?

completionHandler(fileURL)
}

func downloadDidFinish(_ download: WKDownload) {
guard let fileURL = downloadDestinationURL else { return }
activeDownload = nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we update the isDownloadInProgress property here? Currently, it is only set to false when an error occurs.

DispatchQueue.main.async {
let activityVC = UIActivityViewController(activityItems: [fileURL], applicationActivities: nil)
if let rootVC = UIApplication.shared.connectedScenes
.compactMap({ $0 as? UIWindowScene })
.flatMap({ $0.windows })
.first(where: { $0.isKeyWindow })?.rootViewController {
var topVC = rootVC
while let presented = topVC.presentedViewController {
topVC = presented
}
topVC.present(activityVC, animated: true)
}
}
}

func download(_ download: WKDownload, didFailWithError error: Error, resumeData: Data?) {
activeDownload = nil
}
}

// MARK: - WKUIDelegate implementation
extension OSIABWebViewModel: WKUIDelegate {
typealias ButtonHandler = (UIAlertController) -> Void
Expand Down
Loading