File Downloads
This implementation allows users to download .ics files (calendar events) from the LiSA Player running in a WebView, and save them to the device for easy "Add to Calendar" functionality.
Extended WebView with .ics File Download Handling
WebView with .ics File Download Handlingimport SwiftUI
import WebKit
struct WebView: UIViewRepresentable {
// Define the specific UIView subclass we are representing
typealias UIViewType = WKWebView
let urlString: String
func makeUIView(context: Context) -> WKWebView {
// ... shortened ...
}
func updateUIView(_ webView: WKWebView, context: Context) {
// ... shortened ...
}
func makeCoordinator() -> Coordinator {
Coordinator()
}
// Coordinator
// Handles callbacks from the WKWebView
class Coordinator: NSObject, WKScriptMessageHandler, WKNavigationDelegate, WKUIDelegate {
// Reference back to the SwiftUI view
var parent: WebView
// Weak reference maybe better if lifecycle allows?
var webView: WKWebView?
init(_ parent: WebView) {
self.parent = parent
}
// WKScriptMessageHandler
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "MessageFromLiSA" {
// Handle message from JavaScript
print("Received message from LiSA: \(message.body)")
// You could trigger actions in your SwiftUI view here if needed,
// e.g., using @State variables passed down or Combine publishers.
}
}
// Handle navigation actions that become downloads
func webView(_ webView: WKWebView, navigationAction: WKNavigationAction, didBecome download: WKDownload) {
download.delegate = self
print("Download started for: \(navigationAction.request.url?.absoluteString ?? "Unknown URL")")
}
// File download in progress
func download(_ download: WKDownload, didReceive response: URLResponse) {
print("Downloading: \(response.suggestedFilename ?? "Unknown filename")")
}
// Handle file download completion
func download(_ download: WKDownload, didFinishWith location: URL) {
guard let suggestedFilename = download.response?.suggestedFilename else {
print("Unable to retrieve filename")
return
}
let fileManager = FileManager.default
let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
let destinationURL = documentsDirectory.appendingPathComponent(suggestedFilename)
do {
// Move the file to a permanent location
try fileManager.moveItem(at: location, to: destinationURL)
print("File saved to: \(destinationURL)")
// Present options to open the `.ics` file
DispatchQueue.main.async {
let activityViewController = UIActivityViewController(activityItems: [destinationURL], applicationActivities: nil)
if let topController = UIApplication.shared.windows.first?.rootViewController {
topController.present(activityViewController, animated: true, completion: nil)
}
}
} catch {
print("Error saving file: \(error.localizedDescription)")
}
}
// Handle errors during file download
func download(_ download: WKDownload, didFailWithError error: Error, resumeData: Data?) {
print("Download failed: \(error.localizedDescription)")
}
}
}Key Changes and Additions
WKDownloadDelegateImplementationAdded support for
.icsfile downloads using theWKDownloadDelegatemethods.Handles download progress, completion, and errors.
Move
.icsFile to Documents DirectorySaves the
.icsfile to the app'sDocumentsdirectory for user access.
Present
UIActivityViewControllerProvides options to open or share the
.icsfile, e.g., opening it in the Calendar app
Backwards Compatibility for iOS 14+
Ensures the solution works on devices running iOS 14 or later.
Last updated