@@ -20,6 +20,7 @@ import EuriaCore
2020import InfomaniakCore
2121import InfomaniakDI
2222import InfomaniakLogin
23+ import OSLog
2324import Sentry
2425import SwiftUI
2526import UIKit
@@ -29,19 +30,54 @@ import WebKit
2930class EuriaWebViewDelegate : NSObject , ObservableObject {
3031 @Published var isLoaded = false
3132
33+ @Published var isPresentingDocument : URL ?
34+ @Published var error : ErrorDomain ?
35+
3236 let webConfiguration : WKWebViewConfiguration
37+ var downloads = [ WKDownload: URL] ( )
3338
3439 enum Cookie : String {
3540 case userToken = " USER-TOKEN "
3641 case userLanguage = " USER-LANGUAGE "
3742 }
3843
44+ enum ErrorDomain : LocalizedError , Equatable {
45+ case urlGenerationFailed( error: Error )
46+ case downloadFailed( error: Error )
47+
48+ var errorDescription : String ? {
49+ switch self {
50+ case . urlGenerationFailed( let error) :
51+ return error. localizedDescription
52+ case . downloadFailed( let error) :
53+ return error. localizedDescription
54+ }
55+ }
56+
57+ static func == ( lhs: ErrorDomain , rhs: ErrorDomain ) -> Bool {
58+ switch ( lhs, rhs) {
59+ case ( . urlGenerationFailed, . urlGenerationFailed) :
60+ return true
61+ case ( . downloadFailed, . downloadFailed) :
62+ return true
63+ default :
64+ return false
65+ }
66+ }
67+ }
68+
3969 init ( session: any UserSessionable ) {
4070 webConfiguration = WKWebViewConfiguration ( )
4171 super. init ( )
4272 setupWebViewConfiguration ( token: session. apiFetcher. currentToken)
4373 }
4474
75+ deinit {
76+ Task {
77+ await EuriaWebViewDelegate . cleanTemporaryFolder ( )
78+ }
79+ }
80+
4581 private func setupWebViewConfiguration( token: ApiToken ? ) {
4682 addCookies ( token: token)
4783 addUserContentControllers ( )
@@ -77,35 +113,82 @@ class EuriaWebViewDelegate: NSObject, ObservableObject {
77113 ]
78114 )
79115 }
116+
117+ private nonisolated static func cleanTemporaryFolder( ) async {
118+ do {
119+ try FileManager . default. removeItem ( at: URL . temporaryDownloadsDirectory ( ) )
120+ } catch {
121+ Logger . general. error ( " Error while cleaning temporary folder: \( error) " )
122+ }
123+ }
80124}
81125
82126// MARK: - WKNavigationDelegate
83127
84128extension EuriaWebViewDelegate : WKNavigationDelegate {
85- func webView(
86- _ webView : WKWebView ,
87- decidePolicyFor navigationAction : WKNavigationAction ,
88- decisionHandler : @ MainActor ( WKNavigationActionPolicy ) -> Void
89- ) {
129+ func webView( _ webView : WKWebView , decidePolicyFor navigationAction : WKNavigationAction ) async -> WKNavigationActionPolicy {
130+ guard !navigationAction . shouldPerformDownload else {
131+ return . download
132+ }
133+
90134 guard let navigationHost = navigationAction. request. url? . host ( ) else {
91- decisionHandler ( . allow)
92- return
135+ return . allow
93136 }
94137
95138 if navigationHost == ApiEnvironment . current. euriaHost {
96- decisionHandler ( . allow)
97- } else {
98- if navigationAction. navigationType == . linkActivated,
99- let url = navigationAction. request. url {
100- UIApplication . shared. open ( url)
101- }
102- decisionHandler ( . cancel)
139+ return . allow
103140 }
141+
142+ if navigationAction. navigationType == . linkActivated, let url = navigationAction. request. url {
143+ await UIApplication . shared. open ( url)
144+ }
145+ return . cancel
104146 }
105147
106148 func webView( _ webView: WKWebView , didFinish navigation: WKNavigation ! ) {
107149 isLoaded = true
108150 }
151+
152+ func webView( _ webView: WKWebView , navigationAction: WKNavigationAction , didBecome download: WKDownload ) {
153+ download. delegate = self
154+ }
155+ }
156+
157+ // MARK: - WKDownloadDelegate
158+
159+ extension EuriaWebViewDelegate : WKDownloadDelegate {
160+ func download( _ download: WKDownload , decideDestinationUsing response: URLResponse , suggestedFilename: String ) async -> URL ? {
161+ do {
162+ let fileDestinationURL = try URL . temporaryDownloadsDirectory ( ) . appending ( path: suggestedFilename)
163+ guard !FileManager. default. fileExists ( atPath: fileDestinationURL. path ( percentEncoded: false ) ) else {
164+ isPresentingDocument = fileDestinationURL
165+ return nil
166+ }
167+
168+ downloads [ download] = fileDestinationURL
169+ return fileDestinationURL
170+ } catch {
171+ self . error = . urlGenerationFailed( error: error)
172+ Logger . general. error ( " Error while generating the destination URL for a download: \( error) " )
173+ return nil
174+ }
175+ }
176+
177+ func downloadDidFinish( _ download: WKDownload ) {
178+ guard let fileURL = downloads [ download] else {
179+ return
180+ }
181+
182+ isPresentingDocument = fileURL
183+ downloads [ download] = nil
184+ }
185+
186+ func download( _ download: WKDownload , didFailWithError error: any Error , resumeData: Data ? ) {
187+ self . error = . downloadFailed( error: error)
188+ Logger . general. error ( " Error while downloading a file: \( error) " )
189+
190+ downloads [ download] = nil
191+ }
109192}
110193
111194// MARK: - WKScriptMessageHandler
0 commit comments