RxSwift と WKWebView を使ってる時に WKNavigationDelegate を Rx 化したい
今のところこのようになりました。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import RxSwift | |
import RxCocoa | |
import WebKit | |
class WKNavigationDelegateProxy: DelegateProxy, WKNavigationDelegate, DelegateProxyType { | |
static func currentDelegateFor(object: AnyObject) -> AnyObject? { | |
let webView: WKWebView = object as! WKWebView | |
return webView.navigationDelegate | |
} | |
static func setCurrentDelegate(delegate: AnyObject?, toObject object: AnyObject) { | |
let webView: WKWebView = object as! WKWebView | |
webView.navigationDelegate = delegate as? WKNavigationDelegate | |
} | |
} | |
/// | |
/// WKNavigationDelegate protocol のうち、以下 3 メソッドは RxSwift によって | |
/// 引数 decisionHandler が NSBlock に変換されるためうまく扱えないので | |
/// この extension から外しています。 | |
/// - webView(_:didReceiveAuthenticationChallenge:completionHandler:) -> Void | |
/// - webView(_:decidePolicyForNavigationAction:decisionHandler:) -> Void | |
/// - webView(_:decidePolicyForNavigationResponse:decisionHandler:) -> Void | |
/// | |
/// この extention を利用するためには extension 利用側で以下のように実装するか、 | |
/// このファイルの下部にある UIViewController の extention 実装(デフォルト動作になるようにしています)を利用してください。 | |
/// | |
/// ``` | |
/// // viewDidLoad 等の初期化メソッドにて | |
/// let webView: WKWebView = WKWebView(frame: CGRectZero, configuration: WKWebViewConfiguration()) | |
/// | |
/// override func viewDidLoad() { | |
/// super.viewDidLoad() | |
/// | |
/// // この行を追加することで rx_delegate で実装していない protocol があれば self に proxy するようになります | |
/// webView.rx_delegate.setForwardToDelegate(self, retainDelegate: false) | |
/// | |
/// // ... | |
/// } | |
/// | |
/// override func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) { | |
/// // ... | |
/// } | |
/// | |
/// // ... | |
/// ``` | |
/// | |
extension WKWebView { | |
public var rx_delegate: DelegateProxy { | |
return proxyForObject(WKNavigationDelegateProxy.self, self) | |
} | |
public var rx_didCommitNavigation: Observable<(WKWebView, WKNavigation!)> { | |
return rx_delegate.observe(#selector(WKNavigationDelegate.webView(_: didCommitNavigation:))) | |
.map { params in | |
let webView: WKWebView = params[0] as! WKWebView | |
let navigation: WKNavigation! = params[1] as! WKNavigation | |
return (webView, navigation) | |
} | |
} | |
public var rx_didFailNavigation: Observable<(WKWebView, WKNavigation!, NSError)> { | |
return rx_delegate.observe(#selector(WKNavigationDelegate.webView(_: didFailNavigation: withError:))) | |
.map { params in | |
let webView: WKWebView = params[0] as! WKWebView | |
let navigation: WKNavigation! = params[1] as! WKNavigation | |
let error: NSError = params[2] as! NSError | |
return (webView, navigation, error) | |
} | |
} | |
public var rx_didFailProvisionalNavigation: Observable<(WKWebView, WKNavigation!, NSError)> { | |
return rx_delegate.observe(#selector(WKNavigationDelegate.webView(_: didFailProvisionalNavigation: withError:))) | |
.map { params in | |
let webView: WKWebView = params[0] as! WKWebView | |
let navigation: WKNavigation! = params[1] as! WKNavigation | |
let error: NSError = params[2] as! NSError | |
return (webView, navigation, error) | |
} | |
} | |
public var rx_didFinishNavigation: Observable<(WKWebView, WKNavigation!)> { | |
return rx_delegate.observe(#selector(WKNavigationDelegate.webView(_: didFinishNavigation:))) | |
.map { params in | |
let webView: WKWebView = params[0] as! WKWebView | |
let navigation: WKNavigation! = params[1] as! WKNavigation | |
return (webView, navigation) | |
} | |
} | |
public var rx_didReceiveServerRedirectForProvisionalNavigation: Observable<(WKWebView, WKNavigation!)> { | |
return rx_delegate.observe(#selector(WKNavigationDelegate.webView(_: didReceiveServerRedirectForProvisionalNavigation:))) | |
.map { params in | |
let webView: WKWebView = params[0] as! WKWebView | |
let navigation: WKNavigation! = params[1] as! WKNavigation | |
return (webView, navigation) | |
} | |
} | |
public var rx_didStartProvisionalNavigation: Observable<(WKWebView, WKNavigation!)> { | |
return rx_delegate.observe(#selector(WKNavigationDelegate.webView(_: didStartProvisionalNavigation:))) | |
.map { params in | |
let webView: WKWebView = params[0] as! WKWebView | |
let navigation: WKNavigation! = params[1] as! WKNavigation | |
return (webView, navigation) | |
} | |
} | |
public var rx_webContentProcessDidTerminate: Observable<WKWebView> { | |
return rx_delegate.observe(#selector(WKNavigationDelegate.webViewWebContentProcessDidTerminate(_:))) | |
.map { params in | |
let webView: WKWebView = params[0] as! WKWebView | |
return webView | |
} | |
} | |
} | |
/// 上記 WKWebView extension の実装をサポートする extension です。 | |
/// WKNavigationDelegate protocol のデフォルト実装を与えます。 | |
/// 実際の UIViewController のサブクラスで同じメソッドが定義されている場合はそちらが優先されます(override 属性つきになります)。 | |
extension UIViewController { | |
func webView(webView: WKWebView, didReceiveAuthenticationChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) { | |
completionHandler(NSURLSessionAuthChallengeDisposition.PerformDefaultHandling, nil) | |
} | |
func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) { | |
decisionHandler(WKNavigationActionPolicy.Allow) | |
} | |
func webView(webView: WKWebView, decidePolicyForNavigationResponse navigationResponse: WKNavigationResponse, decisionHandler: (WKNavigationResponsePolicy) -> Void) { | |
decisionHandler(WKNavigationResponsePolicy.Allow) | |
} | |
} |
環境
- Xcode 7.3
- Swift 2.2
- RxCocoa 2.3.0
- RxSwift 2.3.0
Related posts: