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

import 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

  1. WKDownloadDelegate Implementation

    • Added support for .ics file downloads using the WKDownloadDelegate methods.

    • Handles download progress, completion, and errors.

  2. Move .ics File to Documents Directory

    • Saves the .ics file to the app's Documents directory for user access.

  3. Present UIActivityViewController

    • Provides options to open or share the .ics file, e.g., opening it in the Calendar app

  4. Backwards Compatibility for iOS 14+

    • Ensures the solution works on devices running iOS 14 or later.

Last updated