11import Combine
2- import WebKit
2+ @ preconcurrency import WebKit
33
44/// View Model containing all the WebView's customisations.
55class OSIABWebViewModel : NSObject , ObservableObject {
@@ -34,7 +34,7 @@ class OSIABWebViewModel: NSObject, ObservableObject {
3434 /// Indicates if the forward button is available for pressing.
3535 @Published private( set) var forwardButtonEnabled : Bool = true
3636
37- /// The current adress label being displayed on the screen. Empty string indicates that the address will not be displayed.
37+ /// The current address label being displayed on the screen. Empty string indicates that the address will not be displayed.
3838 @Published private( set) var addressLabel : String = " "
3939
4040 private var cancellables = Set < AnyCancellable > ( )
@@ -50,10 +50,10 @@ class OSIABWebViewModel: NSObject, ObservableObject {
5050 init (
5151 url: URL ,
5252 customHeaders: [ String : String ] ? = nil ,
53- _ webView: WKWebView ,
54- _ scrollViewBounces: Bool = true ,
55- _ customUserAgent: String ? = nil ,
56- _ backForwardNavigationGestures: Bool = true ,
53+ webView: WKWebView ,
54+ scrollViewBounces: Bool = true ,
55+ customUserAgent: String ? = nil ,
56+ backForwardNavigationGestures: Bool = true ,
5757 uiModel: OSIABWebViewUIModel ,
5858 callbackHandler: OSIABWebViewCallbackHandler
5959 ) {
@@ -62,17 +62,12 @@ class OSIABWebViewModel: NSObject, ObservableObject {
6262 self . webView = webView
6363 self . closeButtonText = uiModel. closeButtonText
6464 self . callbackHandler = callbackHandler
65- if uiModel. showToolbar {
66- self . toolbarPosition = uiModel. toolbarPosition
67- if uiModel. showURL {
68- self . addressLabel = url. absoluteString
69- }
70- } else {
71- self . toolbarPosition = nil
65+ self . toolbarPosition = uiModel. showToolbar ? uiModel. toolbarPosition : nil
66+ if uiModel. showToolbar && uiModel. showURL {
67+ self . addressLabel = url. absoluteString
7268 }
7369 self . showNavigationButtons = uiModel. showNavigationButtons
7470 self . leftToRight = uiModel. leftToRight
75-
7671 super. init ( )
7772 self . webView. allowsBackForwardNavigationGestures = backForwardNavigationGestures
7873 self . webView. scrollView. bounces = scrollViewBounces
@@ -93,20 +88,20 @@ class OSIABWebViewModel: NSObject, ObservableObject {
9388 convenience init (
9489 url: URL ,
9590 customHeaders: [ String : String ] ? = nil ,
96- _ webViewConfiguration: WKWebViewConfiguration ,
97- _ scrollViewBounces: Bool = true ,
98- _ customUserAgent: String ? = nil ,
99- _ backForwardNavigationGestures: Bool = true ,
91+ webViewConfiguration: WKWebViewConfiguration ,
92+ scrollViewBounces: Bool = true ,
93+ customUserAgent: String ? = nil ,
94+ backForwardNavigationGestures: Bool = true ,
10095 uiModel: OSIABWebViewUIModel ,
10196 callbackHandler: OSIABWebViewCallbackHandler
10297 ) {
10398 self . init (
10499 url: url,
105100 customHeaders: customHeaders,
106- WKWebView ( frame: . zero, configuration: webViewConfiguration) ,
107- scrollViewBounces,
108- customUserAgent,
109- backForwardNavigationGestures,
101+ webView : WKWebView ( frame: . zero, configuration: webViewConfiguration) ,
102+ scrollViewBounces: scrollViewBounces ,
103+ customUserAgent: customUserAgent ,
104+ backForwardNavigationGestures: backForwardNavigationGestures ,
110105 uiModel: uiModel,
111106 callbackHandler: callbackHandler
112107 )
@@ -115,50 +110,43 @@ class OSIABWebViewModel: NSObject, ObservableObject {
115110 /// Setups the combine bindings, so that the Published properties can be filled automatically and reactively.
116111 private func setupBindings( _ showURL: Bool , _ showToolbar: Bool , _ showNavigationButtons: Bool ) {
117112 if #available( iOS 14 . 0 , * ) {
118- self . webView. publisher ( for: \. isLoading)
113+ webView. publisher ( for: \. isLoading)
119114 . assign ( to: & $isLoading)
120-
121- self . webView. publisher ( for: \. url)
115+ webView. publisher ( for: \. url)
122116 . compactMap { $0 }
123117 . assign ( to: & $url)
124-
125118 if showToolbar {
126119 if showNavigationButtons {
127- self . webView. publisher ( for: \. canGoBack)
120+ webView. publisher ( for: \. canGoBack)
128121 . assign ( to: & $backButtonEnabled)
129122
130- self . webView. publisher ( for: \. canGoForward)
123+ webView. publisher ( for: \. canGoForward)
131124 . assign ( to: & $forwardButtonEnabled)
132125 }
133-
134126 if showURL {
135- self . $url. map ( \. absoluteString)
127+ $url. map ( \. absoluteString)
136128 . assign ( to: & $addressLabel)
137129 }
138130 }
139131 } else {
140- self . webView. publisher ( for: \. isLoading)
132+ webView. publisher ( for: \. isLoading)
141133 . assign ( to: \. isLoading, on: self )
142134 . store ( in: & cancellables)
143-
144- self . webView. publisher ( for: \. url)
135+ webView. publisher ( for: \. url)
145136 . compactMap { $0 }
146137 . assign ( to: \. url, on: self )
147138 . store ( in: & cancellables)
148-
149139 if showToolbar {
150140 if showNavigationButtons {
151- self . webView. publisher ( for: \. canGoBack)
141+ webView. publisher ( for: \. canGoBack)
152142 . assign ( to: \. backButtonEnabled, on: self )
153143 . store ( in: & cancellables)
154-
155- self . webView. publisher ( for: \. canGoForward)
144+ webView. publisher ( for: \. canGoForward)
156145 . assign ( to: \. forwardButtonEnabled, on: self )
157146 . store ( in: & cancellables)
158147 }
159-
160148 if showURL {
161- self . $url. map ( \. absoluteString)
149+ $url. map ( \. absoluteString)
162150 . assign ( to: \. addressLabel, on: self )
163151 . store ( in: & cancellables)
164152 }
@@ -168,76 +156,69 @@ class OSIABWebViewModel: NSObject, ObservableObject {
168156
169157 /// Loads the URL within the WebView. Is the first operation to be performed when the view is displayed.
170158 func loadURL( ) {
171- var request = URLRequest ( url: self . url)
172- if let headers = self . customHeaders {
173- for (key, value) in headers {
174- request. setValue ( value, forHTTPHeaderField: key)
175- }
159+ var request = URLRequest ( url: url)
160+ customHeaders? . forEach { key, value in
161+ request. setValue ( value, forHTTPHeaderField: key)
176162 }
177- self . webView. load ( request)
163+ webView. load ( request)
178164 }
179165
180166 /// Signals the WebView to move forward. This is performed as a reaction to a button click.
181167 func forwardButtonPressed( ) {
182- self . webView. goForward ( )
168+ webView. goForward ( )
183169 }
184170
185171 /// Signals the WebView to move backwards. This is performed as a reaction to a button click.
186172 func backButtonPressed( ) {
187- self . webView. goBack ( )
173+ webView. goBack ( )
188174 }
189175
190176 /// Signals the WebView to be closed, triggering the `browserClosed` event. This is performed as a reaction to a button click.
191177 func closeButtonPressed( ) {
192- self . callbackHandler. onBrowserClosed ( false )
178+ callbackHandler. onBrowserClosed ( false )
193179 }
194180}
195181
196182// MARK: - WKNavigationDelegate implementation
197183extension OSIABWebViewModel : WKNavigationDelegate {
198184 func webView( _ webView: WKWebView , decidePolicyFor navigationAction: WKNavigationAction , decisionHandler: @escaping ( WKNavigationActionPolicy ) -> Void ) {
199- var shouldStart = true
200-
201185 guard let url = navigationAction. request. url, url == navigationAction. request. mainDocumentURL else { return decisionHandler ( . cancel) }
202186
203187 // if is an app store, tel, sms, mailto or geo link, let the system handle it, otherwise it fails to load it
204188 if [ " itms-appss " , " itms-apps " , " tel " , " sms " , " mailto " , " geo " ] . contains ( url. scheme) {
205189 webView. stopLoading ( )
206- self . callbackHandler. onDelegateURL ( url)
207- shouldStart = false
190+ callbackHandler. onDelegateURL ( url)
191+ decisionHandler ( . cancel)
192+ return
208193 }
209194
210- if shouldStart {
211- if navigationAction. targetFrame != nil {
212- decisionHandler ( . allow)
213- } else {
214- webView. load ( navigationAction. request)
215- decisionHandler ( . cancel)
216- }
195+ if navigationAction. targetFrame != nil {
196+ decisionHandler ( . allow)
217197 } else {
198+ webView. load ( navigationAction. request)
218199 decisionHandler ( . cancel)
219200 }
220201 }
221202
222203 func webView( _ webView: WKWebView , didFinish navigation: WKNavigation ! ) {
223- if !self . firstLoadDone {
224- self . callbackHandler. onBrowserPageLoad ( )
225- self . firstLoadDone = true
204+ if !firstLoadDone {
205+ callbackHandler. onBrowserPageLoad ( )
206+ firstLoadDone = true
226207 } else {
227- self . callbackHandler. onBrowserPageNavigationCompleted ( url. absoluteString)
208+ callbackHandler. onBrowserPageNavigationCompleted ( url. absoluteString)
228209 }
229210 error = nil
230211 }
231212
232213 func webView( _ webView: WKWebView , didFail navigation: WKNavigation ! , withError error: Error ) {
233- self . webView ( webView , didFailedNavigation : " didFailNavigation " , with : error)
214+ handleWebViewNavigationError ( " didFailNavigation " , error : error)
234215 }
235216
236217 func webView( _ webView: WKWebView , didFailProvisionalNavigation navigation: WKNavigation ! , withError error: Error ) {
237- self . webView ( webView , didFailedNavigation : " didFailProvisionalNavigation " , with : error)
218+ handleWebViewNavigationError ( " didFailProvisionalNavigation " , error : error)
238219 }
239220
240- private func webView ( _ webView : WKWebView , didFailedNavigation delegateName: String , with error: Error ) {
221+ private func handleWebViewNavigationError ( _ delegateName: String , error: Error ) {
241222 print ( " webView: \( delegateName) - \( error. localizedDescription) " )
242223 if ( error as NSError ) . code != NSURLErrorCancelled {
243224 self . error = error
@@ -258,7 +239,6 @@ extension OSIABWebViewModel: WKUIDelegate {
258239 private func createAlertController( withBodyText message: String , okButtonHandler: @escaping ButtonHandler , cancelButtonHandler: ButtonHandler ? = nil ) -> UIAlertController {
259240 let title = Bundle . main. infoDictionary ? [ kCFBundleNameKey as String ] as? String ?? " "
260241 let alert = UIAlertController ( title: title, message: message, preferredStyle: . alert)
261-
262242 let okAction = UIAlertAction ( title: " OK " , style: . default) { _ in
263243 okButtonHandler ( alert)
264244 }
@@ -275,42 +255,40 @@ extension OSIABWebViewModel: WKUIDelegate {
275255 }
276256
277257 func webView( _ webView: WKWebView , runJavaScriptAlertPanelWithMessage message: String , initiatedByFrame frame: WKFrameInfo , completionHandler: @escaping ( ) -> Void ) {
278- let result = self . createAlertController (
258+ let result = createAlertController (
279259 withBodyText: message,
280260 okButtonHandler: { alert in
281261 completionHandler ( )
282262 alert. dismiss ( animated: true )
283263 }
284264 )
285- self . callbackHandler. onDelegateAlertController ( result)
265+ callbackHandler. onDelegateAlertController ( result)
286266 }
287267
288268 func webView( _ webView: WKWebView , runJavaScriptConfirmPanelWithMessage message: String , initiatedByFrame frame: WKFrameInfo , completionHandler: @escaping ( Bool ) -> Void ) {
289269 let handler : ( UIAlertController , Bool ) -> Void = { alert, input in
290270 completionHandler ( input)
291271 alert. dismiss ( animated: true )
292272 }
293-
294- let result = self . createAlertController (
273+ let result = createAlertController (
295274 withBodyText: message,
296275 okButtonHandler: { handler ( $0, true ) } ,
297276 cancelButtonHandler: { handler ( $0, false ) }
298277 )
299- self . callbackHandler. onDelegateAlertController ( result)
278+ callbackHandler. onDelegateAlertController ( result)
300279 }
301280
302281 func webView( _ webView: WKWebView , runJavaScriptTextInputPanelWithPrompt prompt: String , defaultText: String ? , initiatedByFrame frame: WKFrameInfo , completionHandler: @escaping ( String ? ) -> Void ) {
303282 let handler : ( UIAlertController , Bool ) -> Void = { alert, returnTextField in
304283 completionHandler ( returnTextField ? alert. textFields? . first? . text : nil )
305284 alert. dismiss ( animated: true )
306285 }
307-
308- let result = self . createAlertController (
286+ let result = createAlertController (
309287 withBodyText: prompt,
310288 okButtonHandler: { handler ( $0, true ) } ,
311289 cancelButtonHandler: { handler ( $0, false ) }
312290 )
313291 result. addTextField { $0. text = defaultText }
314- self . callbackHandler. onDelegateAlertController ( result)
292+ callbackHandler. onDelegateAlertController ( result)
315293 }
316294}
0 commit comments