Skip to content

Instantly share code, notes, and snippets.

@oozoofrog
Created September 3, 2017 15:20
Show Gist options
  • Save oozoofrog/003b7ec6633b64167dd959abe03012e3 to your computer and use it in GitHub Desktop.
Save oozoofrog/003b7ec6633b64167dd959abe03012e3 to your computer and use it in GitHub Desktop.
//
// WKWebView+Rx.swift
// RxGround
//
// Created by eyemac on 2017. 9. 2..
// Copyright © 2017년 rollmind. All rights reserved.
//
import WebKit
import RxSwift
import RxCocoa
public class RxWKNavigationDelegateProxy
: DelegateProxy
, DelegateProxyType
, WKNavigationDelegate {
/// For more information take a look at `DelegateProxyType`.
public override class func createProxyForObject(_ object: AnyObject) -> AnyObject {
guard let webView: WKWebView = object as? WKWebView else { fatalError() }
return webView.createRxNavigationDelegateProxy()
}
/// For more information take a look at `DelegateProxyType`.
public class func setCurrentDelegate(_ delegate: AnyObject?, toObject object: AnyObject) {
guard let webView: WKWebView = object as? WKWebView else { fatalError() }
webView.navigationDelegate = delegate as? WKNavigationDelegate
}
/// For more information take a look at `DelegateProxyType`.
public class func currentDelegateFor(_ object: AnyObject) -> AnyObject? {
guard let webView: WKWebView = object as? WKWebView else { fatalError() }
return webView.navigationDelegate
}
}
public class RxWKUIDelegateProxy
: DelegateProxy
, DelegateProxyType
, WKUIDelegate {
/// For more information take a look at `DelegateProxyType`.
public override class func createProxyForObject(_ object: AnyObject) -> AnyObject {
guard let webView: WKWebView = object as? WKWebView else { fatalError() }
return webView.createRxUIDelegateProxy()
}
/// For more information take a look at `DelegateProxyType`.
public class func setCurrentDelegate(_ delegate: AnyObject?, toObject object: AnyObject) {
guard let webView: WKWebView = object as? WKWebView else { fatalError() }
webView.uiDelegate = delegate as? WKUIDelegate
}
/// For more information take a look at `DelegateProxyType`.
public class func currentDelegateFor(_ object: AnyObject) -> AnyObject? {
guard let webView: WKWebView = object as? WKWebView else { fatalError() }
return webView.uiDelegate
}
}
extension WKWebView {
public func createRxNavigationDelegateProxy() -> RxWKNavigationDelegateProxy {
return RxWKNavigationDelegateProxy(parentObject: self)
}
public func createRxUIDelegateProxy() -> RxWKUIDelegateProxy {
return RxWKUIDelegateProxy(parentObject: self)
}
}
extension Reactive where Base: WKWebView {
public var navigationDelegate: RxWKNavigationDelegateProxy {
return RxWKNavigationDelegateProxy.proxyForObject(base)
}
public var uiDelegate: RxWKUIDelegateProxy {
return RxWKUIDelegateProxy.proxyForObject(base)
}
//MARK: - WKNavigationDelegate
public var didStartProvisionalNavigation: Observable<Void> {
return self.navigationDelegate
.methodInvoked(#selector(WKNavigationDelegate.webView(_:didStartProvisionalNavigation:)))
.map { _ in }
}
public var didFinish: Observable<Void> {
return self.navigationDelegate
.methodInvoked(#selector(WKNavigationDelegate.webView(_:didFinish:)))
.map { _ in }
}
public var didFail: Observable<Void> {
return self.navigationDelegate
.methodInvoked(#selector(WKNavigationDelegate.webView(_:didFail:withError:)))
.map { args in throw args[2] as! Error }
}
public var didFailProvisionalNavigation: Observable<Void> {
return self.navigationDelegate
.methodInvoked(#selector(WKNavigationDelegate.webView(_:didFailProvisionalNavigation:withError:)))
.map { args in throw args[2] as! Error }
}
public var didTerminateWebContentProcess: Observable<Void> {
return self.navigationDelegate
.methodInvoked(#selector(WKNavigationDelegate.webViewWebContentProcessDidTerminate(_:)))
.map { _ in }
}
public typealias ActionPolicyHandle = @convention(block) (WKNavigationActionPolicy) -> Void
public var decideActionPolicy: Observable<(WKNavigationAction, ActionPolicyHandle)> {
return self.navigationDelegate
.methodInvoked(NSSelectorFromString("webView:decidePolicyForNavigationAction:decisionHandler:"))
.map { args in
return (self.cast(args[1]), self.castPointer(args[2])) }
}
public typealias ResponsePolicyHandle = @convention(block) (WKNavigationResponsePolicy) -> Void
public var decideResponsePolicy: Observable<(WKNavigationResponse, ResponsePolicyHandle)> {
return self.navigationDelegate
.methodInvoked(NSSelectorFromString("webView:decidePolicyForNavigationResponse:decisionHandler:"))
.map { args in
return (self.cast(args[1]), self.castPointer(args[2]))
}
}
public var didCommit: Observable<Void> {
return self.navigationDelegate
.methodInvoked(#selector(WKNavigationDelegate.webView(_:didCommit:)))
.map { _ in }
}
public var didReceiveServerRedirectForProvisionalNavigation: Observable<Void> {
return self.navigationDelegate
.methodInvoked(#selector(WKNavigationDelegate.webView(_:didReceiveServerRedirectForProvisionalNavigation:)))
.map { _ in }
}
public typealias AuthChallengeCompletionHandler = @convention(block) (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
public var didReceive: Observable<(URLAuthenticationChallenge, AuthChallengeCompletionHandler)> {
return self.navigationDelegate
.methodInvoked(#selector(WKNavigationDelegate.webView(_:didReceive:completionHandler:)))
.map { args in
return (self.cast(args[1]), self.castPointer(args[2]))
}
}
//MARK: - WKUIDelegate
// public var createWebView: Observable<WKWebView> {
// return self.uiDelegate
// .methodInvoked(#selector(WKUIDelegate.webView(_:createWebViewWith:for:windowFeatures:)))
// .map { args in
// guard let configuration: WKWebViewConfiguration = args[1] as? WKWebViewConfiguration else { fatalError() }
// guard let action: WKNavigationAction = args[2] as? WKNavigationAction else { fatalError() }
// let webView = WKWebView(frame: .zero, configuration: configuration)
// webView.load(action.request)
// return webView
// }
// }
// public var closeWebView: Observable<WKWebView> {
// return self.uiDelegate
// .methodInvoked(#selector(WKUIDelegate.webViewDidClose(_:)))
// .map { args in self.cast(args[1]) }
// }
// public typealias CompletionHandlerWithNoArguments = () -> Void
// public typealias JavaScriptAlertPanelValues = (message: String, frame: WKFrameInfo, completionHandler: CompletionHandlerWithNoArguments)
// public var runJavaScriptAlertPanel: Observable<JavaScriptAlertPanelValues> {
// return self.uiDelegate
// .methodInvoked(#selector(WKUIDelegate.webView(_:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:)))
// .map { args in JavaScriptAlertPanelValues(self.cast(args[1]), self.cast(args[2]), self.castPointer(args[3])) }
// }
//
// public var addWebView: UIBindingObserver<Base, WKWebView> {
// return UIBindingObserver(UIElement: base, binding: { (base, newWebView) in
// newWebView.frame = base.bounds
// newWebView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// base.addSubview(newWebView)
// })
// }
//
// public var removeWebView: UIBindingObserver<Base, WKWebView> {
// return UIBindingObserver(UIElement: base, binding: { (_, newWebView) in
// if newWebView.superview != nil {
// newWebView.removeFromSuperview()
// }
// })
// }
}
extension WKNavigationType: CustomStringConvertible {
public var description: String {
switch self {
case .backForward: return "backForward"
case .formResubmitted: return "formResubmitted"
case .formSubmitted: return "formSubmitted"
case .linkActivated: return "linkActivated"
case .other: return "other"
case .reload: return "reload"
}
}
}
extension Reactive {
public func castPointer<T>(_ any: Any) -> T {
let block = any as AnyObject
let ptr = UnsafeRawPointer(Unmanaged<AnyObject>.passUnretained(block).toOpaque())
let casted = unsafeBitCast(ptr, to: T.self)
return casted
}
public func cast<T>(_ any: Any) -> T {
guard let casted: T = any as? T else { fatalError() }
return casted
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment