├── .circleci └── config.yml ├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── contents.xcworkspacedata ├── Cartfile ├── Cartfile.private ├── Cartfile.resolved ├── Example ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── AuthChallengeViewController.swift ├── Base.lproj │ └── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard ├── FailedRequestViewController.swift ├── Info.plist ├── InvokeJSFunctionViewController.swift ├── JavaScriptAlertPanelViewController.swift ├── JavaScriptConfirmPanelViewController.swift ├── ObservingJSEventViewController.swift ├── OtherRequestViewController.swift ├── RedirectViewController.swift └── ViewController.swift ├── LICENSE.md ├── Package.resolved ├── Package.swift ├── README.md ├── RxWebKit.podspec ├── RxWebKit.xcodeproj ├── project.pbxproj └── xcshareddata │ └── xcschemes │ ├── Example.xcscheme │ ├── RxWebKit.xcscheme │ ├── RxWebKitTests-iOS.xcscheme │ └── RxWebKitTests-macOS.xcscheme ├── RxWebKit.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── RxWebKitTests ├── ForwardsEventsBehavior.swift ├── HasEventsBehavior.swift ├── Info.plist └── RxWebKitTests.swift └── Sources ├── Info.plist ├── RxWebKit.h └── RxWebKit ├── Rx+WebKit.swift ├── RxWKNavigationDelegateProxy.swift ├── RxWKUIDelegateEvents+Rx.swift ├── RxWKUIDelegateProxy.swift ├── RxWKUserContentController.swift └── WKNavigationDelegateEvents+Rx.swift /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | jobs: 4 | "RxWebKit Tests": 5 | working_directory: ~/RxSwiftCommunity/RxWebKit 6 | parallelism: 1 7 | shell: /bin/bash --login 8 | environment: 9 | XCODE_TEST_REPORTS: /tmp/xcode-test-results 10 | LANG: en_US.UTF-8 11 | macos: 12 | xcode: '11.4.1' 13 | steps: 14 | - checkout 15 | - run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS $XCODE_TEST_REPORTS 16 | - restore_cache: 17 | keys: 18 | - v1-dep-{{ .Branch }}- 19 | - v1-dep-master- 20 | - v1-dep- 21 | - run: 22 | name: Bootstrap Carthage 23 | command: carthage update --no-use-binaries 24 | - save_cache: 25 | key: v1-dep-{{ .Branch }}-{{ epoch }} 26 | paths: 27 | - Carthage 28 | - run: 29 | name: Build (Swift 5.0) 30 | command: > 31 | set -o pipefail && xcodebuild build SWIFT_VERSION=5.2.2 32 | -workspace RxWebKit.xcworkspace 33 | -scheme 'RxWebKit' -sdk iphonesimulator 34 | -destination "name=iPhone 11" | xcpretty -c 35 | - run: 36 | name: Run Tests (Swift 5.0) 37 | command: > 38 | set -o pipefail && xcodebuild test SWIFT_VERSION=5.2.2 39 | -workspace RxWebKit.xcworkspace 40 | -scheme 'RxWebKitTests-iOS' -sdk iphonesimulator 41 | -destination "name=iPhone 11" | xcpretty -c --test 42 | - store_artifacts: 43 | path: /tmp/xcode-test-results 44 | workflows: 45 | version: 2 46 | build: 47 | jobs: 48 | - "RxWebKit Tests": 49 | filters: 50 | tags: 51 | ignore: /[0-9]+(\.[0-9]+)*/ 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/swift 3 | 4 | ### Swift ### 5 | # Xcode 6 | # 7 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 8 | 9 | ## Build generated 10 | build/ 11 | DerivedData 12 | 13 | ## Various settings 14 | *.pbxuser 15 | !default.pbxuser 16 | *.mode1v3 17 | !default.mode1v3 18 | *.mode2v3 19 | !default.mode2v3 20 | *.perspectivev3 21 | !default.perspectivev3 22 | xcuserdata 23 | 24 | ## Other 25 | *.xccheckout 26 | *.moved-aside 27 | *.xcuserstate 28 | *.xcscmblueprint 29 | 30 | ## Obj-C/Swift specific 31 | *.hmap 32 | *.ipa 33 | 34 | ## Playgrounds 35 | timeline.xctimeline 36 | playground.xcworkspace 37 | 38 | # Swift Package Manager 39 | # 40 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 41 | # Packages/ 42 | .build/ 43 | 44 | # CocoaPods 45 | # 46 | # We recommend against adding the Pods directory to your .gitignore. However 47 | # you should judge for yourself, the pros and cons are mentioned at: 48 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 49 | # 50 | Pods/ 51 | 52 | # Carthage 53 | # 54 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 55 | Carthage/Checkouts 56 | 57 | Carthage/Build 58 | 59 | # fastlane 60 | # 61 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 62 | # screenshots whenever they are needed. 63 | # For more information about the recommended setup visit: 64 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 65 | 66 | fastlane/report.xml 67 | fastlane/screenshots 68 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "ReactiveX/RxSwift" ~> 6.0 2 | -------------------------------------------------------------------------------- /Cartfile.private: -------------------------------------------------------------------------------- 1 | github "Quick/Quick" 2 | github "Quick/Nimble" 3 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "Quick/Nimble" "v9.0.0" 2 | github "Quick/Quick" "v3.0.0" 3 | github "ReactiveX/RxSwift" "6.0.0" 4 | -------------------------------------------------------------------------------- /Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Example 4 | // 5 | // Created by Daichi Ichihara on 2016/02/06. 6 | // Copyright © 2016年 RxSwift Community. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | private func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Example/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Example/AuthChallengeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthChallengeViewController.swift 3 | // Example 4 | // 5 | // Created by Bob Obi on 25.10.17. 6 | // Copyright © 2017 RxSwift Community. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | import RxWebKit 12 | import RxSwift 13 | import RxCocoa 14 | 15 | class AuthChallengeViewController: UIViewController { 16 | 17 | let bag = DisposeBag() 18 | let wkWebView = WKWebView() 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | view.addSubview(wkWebView) 23 | wkWebView.load(URLRequest(url: URL(string: "https://jigsaw.w3.org/HTTP/Basic/")!)) 24 | 25 | wkWebView.rx 26 | .didReceiveChallenge 27 | .debug("didReceiveChallenge") 28 | .subscribe(onNext: {(webView, challenge, handler) in 29 | guard challenge.previousFailureCount == 0 else { 30 | handler(URLSession.AuthChallengeDisposition.performDefaultHandling, nil) 31 | return 32 | } 33 | /* 34 | The correct credentials are: 35 | 36 | user = guest 37 | password = guest 38 | 39 | You might want to start with the invalid credentials to get a sense of how this code works 40 | */ 41 | let credential = URLCredential(user: "bad-user", password: "bad-password", persistence: URLCredential.Persistence.forSession) 42 | challenge.sender?.use(credential, for: challenge) 43 | handler(URLSession.AuthChallengeDisposition.useCredential, credential) 44 | }) 45 | .disposed(by: bag) 46 | } 47 | 48 | override func viewDidLayoutSubviews() { 49 | super.viewDidLayoutSubviews() 50 | let originY = UIApplication.shared.statusBarFrame.maxY 51 | wkWebView.frame = CGRect(x: 0, y: originY, width: view.bounds.width, height: view.bounds.height) 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /Example/Base.lproj/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Example/Base.lproj/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | -------------------------------------------------------------------------------- /Example/FailedRequestViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FailedRequestViewController.swift 3 | // Example 4 | // 5 | // Created by Bob Godwin Obi on 10/25/17. 6 | // Copyright © 2017 RxSwift Community. All rights reserved. 7 | // 8 | import UIKit 9 | import WebKit 10 | import RxWebKit 11 | import RxSwift 12 | import RxCocoa 13 | 14 | class FailedRequestViewController: UIViewController { 15 | 16 | let bag = DisposeBag() 17 | let wkWebView = WKWebView() 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | view.addSubview(wkWebView) 22 | wkWebView.load(URLRequest(url: URL(string: "https://thiswebsiteisnotexisting.com")!)) 23 | 24 | wkWebView.rx 25 | .didFailProvisionalNavigation 26 | .observe(on: MainScheduler.instance) 27 | .debug("didFailProvisionalNavigation") 28 | .subscribe(onNext: { [weak self] webView, navigation, error in 29 | guard let self = self else { return } 30 | let alert = UIAlertController(title: "FailProvisionalNavigation", message: error.localizedDescription, preferredStyle: .alert) 31 | alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) 32 | self.present(alert, animated: true, completion: nil) 33 | }) 34 | .disposed(by: bag) 35 | } 36 | 37 | override func viewDidLayoutSubviews() { 38 | super.viewDidLayoutSubviews() 39 | let originY = UIApplication.shared.statusBarFrame.maxY 40 | wkWebView.frame = CGRect(x: 0, y: originY, width: view.bounds.width, height: view.bounds.height) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 0.3 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | NSAppTransportSecurity 40 | 41 | NSAllowsArbitraryLoads 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Example/InvokeJSFunctionViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InvokeJSFunctionViewController.swift 3 | // Example 4 | // 5 | // Created by Jesse Hao on 2019/4/1. 6 | // Copyright © 2019 RxSwift Community. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | import RxWebKit 12 | import RxSwift 13 | import RxCocoa 14 | 15 | fileprivate let html = """ 16 | 17 | 18 | 19 | Invoke Javascript function 20 | 21 | 22 | 23 |

Invoke Javascript function

24 |

Just Press 'Invoke' at top right corner.

25 |

After that, pay attention to your console.

26 | 27 | 32 | 33 | 34 | 35 | 36 | """ 37 | 38 | class InvokeJSFunctionViewController : UIViewController { 39 | let bag = DisposeBag() 40 | let webview = WKWebView() 41 | 42 | override func viewDidLoad() { 43 | super.viewDidLoad() 44 | view.addSubview(webview) 45 | self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Invoke", style: .plain, target: nil, action: nil) 46 | self.navigationItem.rightBarButtonItem?.rx.tap 47 | .bind { [weak self] in 48 | guard let self = self else { return } 49 | self.webview.rx.evaluateJavaScript("presentAlert()") 50 | .observe(on: MainScheduler.asyncInstance) 51 | .subscribe { event in 52 | if case .next(let body) = event, let message = body as? String { 53 | print(message) 54 | } 55 | }.disposed(by: self.bag) 56 | } 57 | .disposed(by: self.bag) 58 | webview.loadHTMLString(html, baseURL: nil) 59 | } 60 | 61 | override func viewDidLayoutSubviews() { 62 | super.viewDidLayoutSubviews() 63 | let originY = UIApplication.shared.statusBarFrame.maxY 64 | webview.frame = CGRect(x: 0, y: originY, width: view.bounds.width, height: view.bounds.height) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Example/JavaScriptAlertPanelViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JavaScriptAlertPanelViewController.swift 3 | // Example 4 | // 5 | // Created by Bob Obi on 25.10.17. 6 | // Copyright © 2017 RxSwift Community. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | import RxWebKit 12 | import RxSwift 13 | import RxCocoa 14 | 15 | class JavaScriptAlertPanelViewController: UIViewController { 16 | 17 | let bag = DisposeBag() 18 | let wkWebView = WKWebView() 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | view.addSubview(wkWebView) 23 | wkWebView.load(URLRequest(url: URL(string: "https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_alert")!)) 24 | 25 | wkWebView.rx 26 | .javaScriptAlertPanel 27 | .debug("javaScriptAlertPanel") 28 | .subscribe(onNext: { [weak self] webView, message, frame, handler in 29 | guard let self = self else { return } 30 | let alert = UIAlertController(title: "JavaScriptAlertPanel", message: message, preferredStyle: .alert) 31 | alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) 32 | self.present(alert, animated: true, completion: nil) 33 | handler() 34 | }) 35 | .disposed(by: bag) 36 | } 37 | 38 | override func viewDidLayoutSubviews() { 39 | super.viewDidLayoutSubviews() 40 | let originY = UIApplication.shared.statusBarFrame.maxY 41 | wkWebView.frame = CGRect(x: 0, y: originY, width: view.bounds.width, height: view.bounds.height) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Example/JavaScriptConfirmPanelViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JavaScriptConfirmPanelViewController.swift 3 | // Example 4 | // 5 | // Created by Bob Obi on 25.10.17. 6 | // Copyright © 2017 RxSwift Community. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | import RxWebKit 12 | import RxSwift 13 | import RxCocoa 14 | 15 | class JavaScriptConfirmPanelViewController: UIViewController { 16 | 17 | let bag = DisposeBag() 18 | let wkWebView = WKWebView() 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | view.addSubview(wkWebView) 23 | wkWebView.load(URLRequest(url: URL(string: "https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_confirm2")!)) 24 | 25 | wkWebView.rx 26 | .javaScriptConfirmPanel 27 | .debug("javaScriptConfirmPanel") 28 | .subscribe(onNext: { [weak self] webView, message, frame, handler in 29 | guard let self = self else { return } 30 | let alert = UIAlertController(title: "JavaScriptConfirm", message: message, preferredStyle: .alert) 31 | alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) 32 | self.present(alert, animated: true, completion: nil) 33 | handler(true) 34 | }) 35 | .disposed(by: bag) 36 | } 37 | 38 | override func viewDidLayoutSubviews() { 39 | super.viewDidLayoutSubviews() 40 | let originY = UIApplication.shared.statusBarFrame.maxY 41 | wkWebView.frame = CGRect(x: 0, y: originY, width: view.bounds.width, height: view.bounds.height) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Example/ObservingJSEventViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ObservingJSEventViewController.swift 3 | // Example 4 | // 5 | // Created by Jesse Hao on 2019/4/1. 6 | // Copyright © 2019 RxSwift Community. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | import RxWebKit 12 | import RxSwift 13 | import RxCocoa 14 | 15 | fileprivate let html = """ 16 | 17 | 18 | 19 | 20 | 21 | 22 |

Click the button to display a confirm box.

23 | 24 | 25 | 26 |

27 | 28 | 33 | 34 | 35 | 36 | """ 37 | 38 | class ObservingJSEventViewController : UIViewController { 39 | let bag = DisposeBag() 40 | let webview = WKWebView() 41 | 42 | override func viewDidLoad() { 43 | super.viewDidLoad() 44 | view.addSubview(webview) 45 | webview.configuration.userContentController.rx.scriptMessage(forName: "RxWebKitScriptMessageHandler").bind { [weak self] scriptMessage in 46 | guard let message = scriptMessage.body as? String else { return } 47 | let alert = UIAlertController(title: "JS Event Observed", message: message, preferredStyle: .alert) 48 | alert.addAction(UIAlertAction(title: "OK", style: .cancel)) 49 | self?.present(alert, animated: true) 50 | }.disposed(by: self.bag) 51 | webview.loadHTMLString(html, baseURL: nil) 52 | } 53 | 54 | override func viewDidLayoutSubviews() { 55 | super.viewDidLayoutSubviews() 56 | let originY = UIApplication.shared.statusBarFrame.maxY 57 | webview.frame = CGRect(x: 0, y: originY, width: view.bounds.width, height: view.bounds.height) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Example/OtherRequestViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OtherRequestViewController.swift 3 | // Example 4 | // 5 | // Created by Bob Godwin Obi on 10/25/17. 6 | // Copyright © 2017 RxSwift Community. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | import RxWebKit 12 | import RxSwift 13 | import RxCocoa 14 | 15 | class OtherRequestViewController: UIViewController { 16 | 17 | let bag = DisposeBag() 18 | let wkWebView = WKWebView() 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | view.addSubview(wkWebView) 23 | wkWebView.load(URLRequest(url: URL(string: "https://github.com/ReactiveX/RxSwift")!)) 24 | 25 | wkWebView.rx 26 | .didFinishNavigation 27 | .debug("didFinishNavigation") 28 | .subscribe(onNext: {_ in }) 29 | .disposed(by: bag) 30 | 31 | if #available(iOS 9.0, *) { 32 | wkWebView.rx 33 | .didTerminate 34 | .debug("didTerminate") 35 | .subscribe(onNext: {_ in }) 36 | .disposed(by: bag) 37 | } 38 | 39 | wkWebView.rx 40 | .didCommitNavigation 41 | .debug("didCommitNavigation") 42 | .subscribe(onNext: {_ in }) 43 | .disposed(by: bag) 44 | 45 | wkWebView.rx 46 | .didStartProvisionalNavigation 47 | .debug("didStartProvisionalNavigation") 48 | .subscribe(onNext: {_ in}) 49 | .disposed(by: bag) 50 | 51 | if #available(iOS 10.0, *) { 52 | wkWebView.rx 53 | .commitPreviewing 54 | .debug("commitPreviewing") 55 | .subscribe(onNext:{_ in}) 56 | .disposed(by: bag) 57 | } 58 | 59 | wkWebView.rx 60 | .decidePolicyNavigationResponse 61 | .debug("decidePolicyNavigationResponse") 62 | .subscribe(onNext: {(_, _, handler) in 63 | handler(.allow) 64 | }) 65 | .disposed(by: bag) 66 | 67 | wkWebView.rx 68 | .decidePolicyNavigationAction 69 | .debug("decidePolicyNavigationAction") 70 | .subscribe(onNext: {(_, _, handler) in 71 | handler(.allow) 72 | }) 73 | .disposed(by: bag) 74 | } 75 | 76 | override func viewDidLayoutSubviews() { 77 | super.viewDidLayoutSubviews() 78 | let originY = UIApplication.shared.statusBarFrame.maxY 79 | wkWebView.frame = CGRect(x: 0, y: originY, width: view.bounds.width, height: view.bounds.height) 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /Example/RedirectViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RedirectViewController.swift 3 | // Example 4 | // 5 | // Created by Bob Godwin Obi on 10/25/17. 6 | // Copyright © 2017 RxSwift Community. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | import RxWebKit 12 | import RxSwift 13 | import RxCocoa 14 | 15 | class RedirectViewController: UIViewController { 16 | 17 | let bag = DisposeBag() 18 | let wkWebView = WKWebView() 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | view.addSubview(wkWebView) 23 | wkWebView.load(URLRequest(url: URL(string: "http://www.webconfs.com/http-header-check.php")!)) 24 | 25 | wkWebView.rx 26 | .didReceiveServerRedirectForProvisionalNavigation 27 | .observe(on: MainScheduler.instance) 28 | .debug("didReceiveServerRedirectForProvisionalNavigation") 29 | .subscribe(onNext: { [weak self] webView, navigation in 30 | guard let self = self else { return } 31 | let alert = UIAlertController(title: "Redirect Navigation", message: "you have bene redirected", preferredStyle: .alert) 32 | alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) 33 | self.present(alert, animated: true, completion: nil) 34 | }) 35 | .disposed(by: bag) 36 | } 37 | 38 | override func viewDidLayoutSubviews() { 39 | super.viewDidLayoutSubviews() 40 | let originY = UIApplication.shared.statusBarFrame.maxY 41 | wkWebView.frame = CGRect(x: 0, y: originY, width: view.bounds.width, height: view.bounds.height) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Example/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Example 4 | // 5 | // Created by Daichi Ichihara on 2016/02/06. 6 | // Copyright © 2016年 RxSwift Community. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebKit 11 | import RxWebKit 12 | import RxSwift 13 | import RxCocoa 14 | 15 | class ViewController: UIViewController { 16 | 17 | @IBOutlet weak var toolBar: UIToolbar! 18 | @IBOutlet weak var backButton: UIBarButtonItem! 19 | @IBOutlet weak var forwardButton: UIBarButtonItem! 20 | 21 | var wkWebView = WKWebView() 22 | let disposeBag = DisposeBag() 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | self.view.addSubview(wkWebView) 27 | let request = URLRequest(url: URL(string: "https://github.com/ReactiveX/RxSwift")!) 28 | wkWebView.load(request) 29 | 30 | observeReadOnlyProperties(wkWebView: wkWebView) 31 | observeToolBarButtonItems() 32 | } 33 | 34 | override func viewDidLayoutSubviews() { 35 | super.viewDidLayoutSubviews() 36 | let originY = UIApplication.shared.statusBarFrame.maxY 37 | wkWebView.frame = CGRect( 38 | x: 0, 39 | y: originY, 40 | width: self.view.bounds.width, 41 | height: toolBar.frame.minY - originY 42 | ) 43 | } 44 | 45 | private func observeReadOnlyProperties(wkWebView: WKWebView) { 46 | wkWebView.rx.title 47 | .share(replay: 1) 48 | .subscribe(onNext: { 49 | print("title: \(String(describing: $0))") 50 | }) 51 | .disposed(by: disposeBag) 52 | 53 | wkWebView.rx.url 54 | .share(replay: 1) 55 | .subscribe(onNext: { 56 | print("URL: \(String(describing: $0))") 57 | }) 58 | .disposed(by: disposeBag) 59 | 60 | wkWebView.rx.estimatedProgress 61 | .share(replay: 1) 62 | .subscribe(onNext: { 63 | print("estimatedProgress: \($0)") 64 | }) 65 | .disposed(by: disposeBag) 66 | 67 | wkWebView.rx.loading 68 | .share(replay: 1) 69 | .subscribe(onNext: { 70 | print("loading: \($0)") 71 | }) 72 | .disposed(by: disposeBag) 73 | 74 | wkWebView.rx.canGoBack 75 | .share(replay: 1) 76 | .subscribe(onNext: { [weak self] in 77 | self?.backButton.isEnabled = $0 78 | }) 79 | .disposed(by: disposeBag) 80 | 81 | wkWebView.rx.canGoForward 82 | .share(replay: 1) 83 | .subscribe(onNext: { [weak self] in 84 | self?.forwardButton.isEnabled = $0 85 | }) 86 | .disposed(by: disposeBag) 87 | } 88 | 89 | private func observeToolBarButtonItems() { 90 | backButton.rx.tap 91 | .share(replay: 1) 92 | .subscribe(onNext: { [weak self] in 93 | _ = self?.wkWebView.goBack() 94 | }) 95 | .disposed(by: disposeBag) 96 | 97 | forwardButton.rx.tap 98 | .share(replay: 1) 99 | .subscribe(onNext: { [weak self] in 100 | _ = self?.wkWebView.goForward() 101 | }) 102 | .disposed(by: disposeBag) 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | **The MIT License** 2 | **Copyright © 2016 Daichi Ichihara ** 3 | **All rights reserved.** 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "RxSwift", 6 | "repositoryURL": "https://github.com/ReactiveX/RxSwift.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "b4307ba0b6425c0ba4178e138799946c3da594f8", 10 | "version": "6.5.0" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "RxWebKit", 6 | platforms: [.iOS(.v9), .macOS(.v10_13)], 7 | products: [ 8 | .library(name: "RxWebKit", targets: ["RxWebKit"]) 9 | ], 10 | dependencies: [ 11 | .package(url: "https://github.com/ReactiveX/RxSwift.git", .upToNextMajor(from: "6.0.0")) 12 | ], 13 | targets: [ 14 | .target( 15 | name: "RxWebKit", 16 | dependencies: ["RxSwift", "RxCocoa"] 17 | ) 18 | ], 19 | swiftLanguageVersions: [.v5] 20 | ) 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RxWebKit 2 | 3 | RxWebKit is a [RxSwift](https://github.com/ReactiveX/RxSwift) wrapper for `WebKit`. 4 | 5 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 6 | [![Version](https://img.shields.io/cocoapods/v/RxWebKit.svg?style=flat)](http://cocoapods.org/pods/RxWebKit) 7 | [![License](https://img.shields.io/cocoapods/l/RxWebKit.svg?style=flat)](http://cocoapods.org/pods/RxWebKit) 8 | [![Platform](https://img.shields.io/cocoapods/p/RxWebKit.svg?style=flat)](http://cocoapods.org/pods/RxWebKit) 9 | 10 | ## Example Usages 11 | 12 | ```swift 13 | // MARK: Setup WKWebView 14 | 15 | let webView = WKWebView(frame: self.view.bounds) 16 | self.view.addSubview(webView) 17 | 18 | 19 | // MARK: Observing properties 20 | 21 | webView.rx.title 22 | .subscribe(onNext: { 23 | print("title: \($0)") 24 | }) 25 | .disposed(by: disposeBag) 26 | 27 | webView.rx.url 28 | .subscribe(onNext: { 29 | print("URL: \($0)") 30 | }) 31 | .disposed(by: disposeBag) 32 | ``` 33 | 34 | ## Installation 35 | 36 | ### CocoaPods 37 | 38 | Add to `Podfile`: 39 | 40 | ``` 41 | pod 'RxWebKit' 42 | ``` 43 | 44 | ### Carthage 45 | 46 | Add to `Cartfile`: 47 | 48 | ``` 49 | github "RxSwiftCommunity/RxWebKit" 50 | ``` 51 | 52 | Run `carthage update --platform iOS` 53 | 54 | Add run script build phase `/usr/local/bin/carthage copy-frameworks` with input files being: 55 | 56 | ``` 57 | $(SRCROOT)/carthage/Build/iOS/RxWebKit.framework 58 | ``` 59 | 60 | ## Requirements 61 | 62 | RxWebKit requires Swift 5.2.2 and dedicated versions of RxSwift 6.0.0 63 | 64 | ## License 65 | 66 | MIT 67 | -------------------------------------------------------------------------------- /RxWebKit.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "RxWebKit" 3 | s.version = "2.0.1" 4 | s.summary = "RxWebKit is a RxSwift wrapper for WebKit." 5 | s.description = <<-DESC 6 | RxWebKit is a RxSwift wrapper for `WebKit`. 7 | 8 | ```swift 9 | // MARK: Setup WKWebView 10 | 11 | let webView = WKWebView(frame: self.view.bounds) 12 | self.view.addSubview(webView) 13 | 14 | 15 | // MARK: Observing properties 16 | 17 | webView.rx.title 18 | .subscribe(onNext: { 19 | print("title: \($0)") 20 | }) 21 | .disposed(by: disposeBag) 22 | 23 | webView.rx.url 24 | .subscribe(onNext: { 25 | print("URL: \($0)") 26 | }) 27 | .disposed(by: disposeBag) 28 | ``` 29 | DESC 30 | 31 | s.homepage = "https://github.com/RxSwiftCommunity/RxWebKit" 32 | s.license = "MIT" 33 | s.authors = { "mokumoku" => "da1lawmoku2@gmail.com", 34 | "RxSwift Community" => "community@rxswift.org" 35 | } 36 | s.source = { :git => "https://github.com/RxSwiftCommunity/RxWebKit.git", :tag => s.version.to_s } 37 | s.source_files = "Sources/RxWebKit/**/*.{swift}" 38 | s.exclude_files = "Sources/RxWebKit/**/*.{plist}" 39 | s.ios.deployment_target = '9.0' 40 | s.osx.deployment_target = '10.13' 41 | s.swift_version = '5.0' 42 | s.dependency 'RxSwift', '~> 6.0' 43 | s.dependency 'RxCocoa', '~> 6.0' 44 | end 45 | -------------------------------------------------------------------------------- /RxWebKit.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 42F426B61C66223E001FED46 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42F426AD1C66223E001FED46 /* AppDelegate.swift */; }; 11 | 42F426B71C66223E001FED46 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 42F426AE1C66223E001FED46 /* Assets.xcassets */; }; 12 | 42F426B81C66223E001FED46 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 42F426B01C66223E001FED46 /* LaunchScreen.storyboard */; }; 13 | 42F426B91C66223E001FED46 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 42F426B21C66223E001FED46 /* Main.storyboard */; }; 14 | 42F426BA1C66223E001FED46 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42F426B41C66223E001FED46 /* ViewController.swift */; }; 15 | 42F426D11C6623C5001FED46 /* RxWebKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 42F426D01C6623C5001FED46 /* RxWebKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 16 | 42F426D61C6623C5001FED46 /* RxWebKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 42F426CE1C6623C5001FED46 /* RxWebKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 17 | 42F426DC1C662CF9001FED46 /* Rx+WebKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42F426DB1C662CF9001FED46 /* Rx+WebKit.swift */; }; 18 | C4135FFE22875FBE0061933C /* RxWebKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CB7D5322874EE700FB8D99 /* RxWebKitTests.swift */; }; 19 | C4136011228764840061933C /* Nimble.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C4D323B3228748A0004B05A5 /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 20 | C4136012228764840061933C /* Quick.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C4D323B2228748A0004B05A5 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 21 | C4136013228764840061933C /* RxTest.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C4D323B4228748A0004B05A5 /* RxTest.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 22 | C4136014228764840061933C /* RxSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 424205831C89E9DF00214DA1 /* RxSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 23 | C4136015228764840061933C /* RxCocoa.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 424205841C89E9DF00214DA1 /* RxCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 24 | C4136018228765250061933C /* RxRelay.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C41360162287651B0061933C /* RxRelay.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 25 | C413601A2288135F0061933C /* HasEventsBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41360192288135F0061933C /* HasEventsBehavior.swift */; }; 26 | C413602522896CC20061933C /* RxSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 424205831C89E9DF00214DA1 /* RxSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 27 | C413602622896CC20061933C /* RxCocoa.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 424205841C89E9DF00214DA1 /* RxCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 28 | C413602722896CE20061933C /* RxRelay.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C41360162287651B0061933C /* RxRelay.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 29 | C4607FD01FA0DAC9002DA12F /* RxWKUIDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4607FCF1FA0DAC9002DA12F /* RxWKUIDelegateProxy.swift */; }; 30 | C4607FD61FA0DC40002DA12F /* RxWKUIDelegateEvents+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4607FD51FA0DC40002DA12F /* RxWKUIDelegateEvents+Rx.swift */; }; 31 | C4607FDB1FA0E68F002DA12F /* JavaScriptAlertPanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4607FDA1FA0E68F002DA12F /* JavaScriptAlertPanelViewController.swift */; }; 32 | C4607FDD1FA0F1D3002DA12F /* JavaScriptConfirmPanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4607FDC1FA0F1D3002DA12F /* JavaScriptConfirmPanelViewController.swift */; }; 33 | C4607FE11FA0F549002DA12F /* AuthChallengeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4607FE01FA0F549002DA12F /* AuthChallengeViewController.swift */; }; 34 | C4878CD61FA1066C00B12C60 /* FailedRequestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4878CD51FA1066C00B12C60 /* FailedRequestViewController.swift */; }; 35 | C4878CD81FA117E900B12C60 /* RedirectViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4878CD71FA117E900B12C60 /* RedirectViewController.swift */; }; 36 | C4878CDA1FA11B5A00B12C60 /* OtherRequestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4878CD91FA11B5A00B12C60 /* OtherRequestViewController.swift */; }; 37 | C48E617E1F9E27A700C6A753 /* RxWKNavigationDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48E617D1F9E27A700C6A753 /* RxWKNavigationDelegateProxy.swift */; }; 38 | C48E61841F9E336000C6A753 /* WKNavigationDelegateEvents+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48E61831F9E336000C6A753 /* WKNavigationDelegateEvents+Rx.swift */; }; 39 | C4CA6B07228AB1680088A7A3 /* ForwardsEventsBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CA6B06228AB1680088A7A3 /* ForwardsEventsBehavior.swift */; }; 40 | EFC27F84246D7E9C0093D8B3 /* RxWebKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CB7D5322874EE700FB8D99 /* RxWebKitTests.swift */; }; 41 | EFC27F85246D7E9C0093D8B3 /* ForwardsEventsBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CA6B06228AB1680088A7A3 /* ForwardsEventsBehavior.swift */; }; 42 | EFC27F86246D7E9C0093D8B3 /* HasEventsBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41360192288135F0061933C /* HasEventsBehavior.swift */; }; 43 | EFC27F96246D7F160093D8B3 /* Nimble.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EFC27F95246D7F160093D8B3 /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 44 | EFC27F98246D7F1A0093D8B3 /* Quick.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EFC27F97246D7F1A0093D8B3 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 45 | EFC27F9A246D7F220093D8B3 /* RxCocoa.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EFC27F99246D7F220093D8B3 /* RxCocoa.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 46 | EFC27F9C246D7F290093D8B3 /* RxRelay.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EFC27F9B246D7F290093D8B3 /* RxRelay.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 47 | EFC27F9E246D7F2D0093D8B3 /* RxSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EFC27F9D246D7F2D0093D8B3 /* RxSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 48 | EFC27FA0246D7F310093D8B3 /* RxTest.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EFC27F9F246D7F300093D8B3 /* RxTest.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 49 | F68CEB9E22521B1D00498607 /* InvokeJSFunctionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F68CEB9D22521B1D00498607 /* InvokeJSFunctionViewController.swift */; }; 50 | F68CEBA022521B2B00498607 /* ObservingJSEventViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F68CEB9F22521B2B00498607 /* ObservingJSEventViewController.swift */; }; 51 | F6D7DD0D224F62860066C90F /* RxWKUserContentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6D7DD0C224F62860066C90F /* RxWKUserContentController.swift */; }; 52 | /* End PBXBuildFile section */ 53 | 54 | /* Begin PBXContainerItemProxy section */ 55 | 42F426D31C6623C5001FED46 /* PBXContainerItemProxy */ = { 56 | isa = PBXContainerItemProxy; 57 | containerPortal = 42F4268F1C66200D001FED46 /* Project object */; 58 | proxyType = 1; 59 | remoteGlobalIDString = 42F426CD1C6623C5001FED46; 60 | remoteInfo = RxWebKit; 61 | }; 62 | C4CB7D4922874E5600FB8D99 /* PBXContainerItemProxy */ = { 63 | isa = PBXContainerItemProxy; 64 | containerPortal = 42F4268F1C66200D001FED46 /* Project object */; 65 | proxyType = 1; 66 | remoteGlobalIDString = 42F426CD1C6623C5001FED46; 67 | remoteInfo = RxWebKit; 68 | }; 69 | EFC27F82246D7E9C0093D8B3 /* PBXContainerItemProxy */ = { 70 | isa = PBXContainerItemProxy; 71 | containerPortal = 42F4268F1C66200D001FED46 /* Project object */; 72 | proxyType = 1; 73 | remoteGlobalIDString = 42F426CD1C6623C5001FED46; 74 | remoteInfo = RxWebKit; 75 | }; 76 | /* End PBXContainerItemProxy section */ 77 | 78 | /* Begin PBXCopyFilesBuildPhase section */ 79 | 42F426DA1C6623C5001FED46 /* Embed Frameworks */ = { 80 | isa = PBXCopyFilesBuildPhase; 81 | buildActionMask = 2147483647; 82 | dstPath = ""; 83 | dstSubfolderSpec = 10; 84 | files = ( 85 | 42F426D61C6623C5001FED46 /* RxWebKit.framework in Embed Frameworks */, 86 | C413602522896CC20061933C /* RxSwift.framework in Embed Frameworks */, 87 | C413602622896CC20061933C /* RxCocoa.framework in Embed Frameworks */, 88 | C413602722896CE20061933C /* RxRelay.framework in Embed Frameworks */, 89 | ); 90 | name = "Embed Frameworks"; 91 | runOnlyForDeploymentPostprocessing = 0; 92 | }; 93 | C41360102287644B0061933C /* Embed Frameworks */ = { 94 | isa = PBXCopyFilesBuildPhase; 95 | buildActionMask = 2147483647; 96 | dstPath = ""; 97 | dstSubfolderSpec = 10; 98 | files = ( 99 | C4136011228764840061933C /* Nimble.framework in Embed Frameworks */, 100 | C4136012228764840061933C /* Quick.framework in Embed Frameworks */, 101 | C4136013228764840061933C /* RxTest.framework in Embed Frameworks */, 102 | C4136014228764840061933C /* RxSwift.framework in Embed Frameworks */, 103 | C4136015228764840061933C /* RxCocoa.framework in Embed Frameworks */, 104 | C4136018228765250061933C /* RxRelay.framework in Embed Frameworks */, 105 | ); 106 | name = "Embed Frameworks"; 107 | runOnlyForDeploymentPostprocessing = 0; 108 | }; 109 | EFC27F89246D7E9C0093D8B3 /* Embed Frameworks */ = { 110 | isa = PBXCopyFilesBuildPhase; 111 | buildActionMask = 2147483647; 112 | dstPath = ""; 113 | dstSubfolderSpec = 10; 114 | files = ( 115 | EFC27FA0246D7F310093D8B3 /* RxTest.framework in Embed Frameworks */, 116 | EFC27F9E246D7F2D0093D8B3 /* RxSwift.framework in Embed Frameworks */, 117 | EFC27F9C246D7F290093D8B3 /* RxRelay.framework in Embed Frameworks */, 118 | EFC27F96246D7F160093D8B3 /* Nimble.framework in Embed Frameworks */, 119 | EFC27F98246D7F1A0093D8B3 /* Quick.framework in Embed Frameworks */, 120 | EFC27F9A246D7F220093D8B3 /* RxCocoa.framework in Embed Frameworks */, 121 | ); 122 | name = "Embed Frameworks"; 123 | runOnlyForDeploymentPostprocessing = 0; 124 | }; 125 | /* End PBXCopyFilesBuildPhase section */ 126 | 127 | /* Begin PBXFileReference section */ 128 | 424205831C89E9DF00214DA1 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/iOS/RxSwift.framework; sourceTree = ""; }; 129 | 424205841C89E9DF00214DA1 /* RxCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxCocoa.framework; path = Carthage/Build/iOS/RxCocoa.framework; sourceTree = ""; }; 130 | 42F426971C66200D001FED46 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 131 | 42F426AC1C66223E001FED46 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 132 | 42F426AD1C66223E001FED46 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 133 | 42F426AE1C66223E001FED46 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 134 | 42F426B11C66223E001FED46 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 135 | 42F426B31C66223E001FED46 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 136 | 42F426B41C66223E001FED46 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 137 | 42F426CE1C6623C5001FED46 /* RxWebKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxWebKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 138 | 42F426D01C6623C5001FED46 /* RxWebKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RxWebKit.h; sourceTree = ""; }; 139 | 42F426D21C6623C5001FED46 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 140 | 42F426DB1C662CF9001FED46 /* Rx+WebKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rx+WebKit.swift"; sourceTree = ""; }; 141 | C41360162287651B0061933C /* RxRelay.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxRelay.framework; path = Carthage/Build/iOS/RxRelay.framework; sourceTree = ""; }; 142 | C41360192288135F0061933C /* HasEventsBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HasEventsBehavior.swift; sourceTree = ""; }; 143 | C4607FCF1FA0DAC9002DA12F /* RxWKUIDelegateProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxWKUIDelegateProxy.swift; sourceTree = ""; }; 144 | C4607FD51FA0DC40002DA12F /* RxWKUIDelegateEvents+Rx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RxWKUIDelegateEvents+Rx.swift"; sourceTree = ""; }; 145 | C4607FDA1FA0E68F002DA12F /* JavaScriptAlertPanelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JavaScriptAlertPanelViewController.swift; sourceTree = ""; }; 146 | C4607FDC1FA0F1D3002DA12F /* JavaScriptConfirmPanelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JavaScriptConfirmPanelViewController.swift; sourceTree = ""; }; 147 | C4607FE01FA0F549002DA12F /* AuthChallengeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthChallengeViewController.swift; sourceTree = ""; }; 148 | C4878CD51FA1066C00B12C60 /* FailedRequestViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailedRequestViewController.swift; sourceTree = ""; }; 149 | C4878CD71FA117E900B12C60 /* RedirectViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedirectViewController.swift; sourceTree = ""; }; 150 | C4878CD91FA11B5A00B12C60 /* OtherRequestViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OtherRequestViewController.swift; sourceTree = ""; }; 151 | C48E617D1F9E27A700C6A753 /* RxWKNavigationDelegateProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxWKNavigationDelegateProxy.swift; sourceTree = ""; }; 152 | C48E61831F9E336000C6A753 /* WKNavigationDelegateEvents+Rx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WKNavigationDelegateEvents+Rx.swift"; sourceTree = ""; }; 153 | C4A50F9E229AF3EF0037E608 /* config.yml */ = {isa = PBXFileReference; lastKnownFileType = text; name = config.yml; path = .circleci/config.yml; sourceTree = SOURCE_ROOT; }; 154 | C4CA6B06228AB1680088A7A3 /* ForwardsEventsBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForwardsEventsBehavior.swift; sourceTree = ""; }; 155 | C4CA6B15228D752C0088A7A3 /* RxWebKit.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = RxWebKit.podspec; sourceTree = SOURCE_ROOT; }; 156 | C4CA6B16228D75410088A7A3 /* Cartfile.private */ = {isa = PBXFileReference; lastKnownFileType = text; path = Cartfile.private; sourceTree = SOURCE_ROOT; }; 157 | C4CA6B17228D75420088A7A3 /* Cartfile.resolved */ = {isa = PBXFileReference; lastKnownFileType = text; path = Cartfile.resolved; sourceTree = SOURCE_ROOT; }; 158 | C4CA6B18228D75420088A7A3 /* Cartfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Cartfile; sourceTree = SOURCE_ROOT; }; 159 | C4CB7D4322874E5600FB8D99 /* RxWebKitTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RxWebKitTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 160 | C4CB7D4722874E5600FB8D99 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 161 | C4CB7D5322874EE700FB8D99 /* RxWebKitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxWebKitTests.swift; sourceTree = ""; }; 162 | C4D323B2228748A0004B05A5 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/iOS/Quick.framework; sourceTree = ""; }; 163 | C4D323B3228748A0004B05A5 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/iOS/Nimble.framework; sourceTree = ""; }; 164 | C4D323B4228748A0004B05A5 /* RxTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxTest.framework; path = Carthage/Build/iOS/RxTest.framework; sourceTree = ""; }; 165 | EFC27F93246D7E9C0093D8B3 /* RxWebKitTests-macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RxWebKitTests-macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 166 | EFC27F95246D7F160093D8B3 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/Mac/Nimble.framework; sourceTree = ""; }; 167 | EFC27F97246D7F1A0093D8B3 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/Mac/Quick.framework; sourceTree = ""; }; 168 | EFC27F99246D7F220093D8B3 /* RxCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxCocoa.framework; path = Carthage/Build/Mac/RxCocoa.framework; sourceTree = ""; }; 169 | EFC27F9B246D7F290093D8B3 /* RxRelay.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxRelay.framework; path = Carthage/Build/Mac/RxRelay.framework; sourceTree = ""; }; 170 | EFC27F9D246D7F2D0093D8B3 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/Mac/RxSwift.framework; sourceTree = ""; }; 171 | EFC27F9F246D7F300093D8B3 /* RxTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxTest.framework; path = Carthage/Build/Mac/RxTest.framework; sourceTree = ""; }; 172 | F68CEB9D22521B1D00498607 /* InvokeJSFunctionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvokeJSFunctionViewController.swift; sourceTree = ""; }; 173 | F68CEB9F22521B2B00498607 /* ObservingJSEventViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservingJSEventViewController.swift; sourceTree = ""; }; 174 | F6D7DD0C224F62860066C90F /* RxWKUserContentController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxWKUserContentController.swift; sourceTree = ""; }; 175 | /* End PBXFileReference section */ 176 | 177 | /* Begin PBXFrameworksBuildPhase section */ 178 | 42F426941C66200D001FED46 /* Frameworks */ = { 179 | isa = PBXFrameworksBuildPhase; 180 | buildActionMask = 2147483647; 181 | files = ( 182 | ); 183 | runOnlyForDeploymentPostprocessing = 0; 184 | }; 185 | 42F426CA1C6623C5001FED46 /* Frameworks */ = { 186 | isa = PBXFrameworksBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | ); 190 | runOnlyForDeploymentPostprocessing = 0; 191 | }; 192 | C4CB7D4022874E5600FB8D99 /* Frameworks */ = { 193 | isa = PBXFrameworksBuildPhase; 194 | buildActionMask = 2147483647; 195 | files = ( 196 | ); 197 | runOnlyForDeploymentPostprocessing = 0; 198 | }; 199 | EFC27F87246D7E9C0093D8B3 /* Frameworks */ = { 200 | isa = PBXFrameworksBuildPhase; 201 | buildActionMask = 2147483647; 202 | files = ( 203 | ); 204 | runOnlyForDeploymentPostprocessing = 0; 205 | }; 206 | /* End PBXFrameworksBuildPhase section */ 207 | 208 | /* Begin PBXGroup section */ 209 | 42B12EFB1C6DFC7600C280BD /* Sources */ = { 210 | isa = PBXGroup; 211 | children = ( 212 | 42F426DB1C662CF9001FED46 /* Rx+WebKit.swift */, 213 | C48E61831F9E336000C6A753 /* WKNavigationDelegateEvents+Rx.swift */, 214 | C48E617D1F9E27A700C6A753 /* RxWKNavigationDelegateProxy.swift */, 215 | C4607FCF1FA0DAC9002DA12F /* RxWKUIDelegateProxy.swift */, 216 | C4607FD51FA0DC40002DA12F /* RxWKUIDelegateEvents+Rx.swift */, 217 | F6D7DD0C224F62860066C90F /* RxWKUserContentController.swift */, 218 | ); 219 | path = Sources; 220 | sourceTree = ""; 221 | }; 222 | 42F4268E1C66200D001FED46 = { 223 | isa = PBXGroup; 224 | children = ( 225 | EFC27F9F246D7F300093D8B3 /* RxTest.framework */, 226 | EFC27F9D246D7F2D0093D8B3 /* RxSwift.framework */, 227 | EFC27F9B246D7F290093D8B3 /* RxRelay.framework */, 228 | EFC27F99246D7F220093D8B3 /* RxCocoa.framework */, 229 | EFC27F97246D7F1A0093D8B3 /* Quick.framework */, 230 | EFC27F95246D7F160093D8B3 /* Nimble.framework */, 231 | 42F426991C66200D001FED46 /* Example */, 232 | 733B51F3D0F2B4B1E9A99FDC /* Frameworks */, 233 | 42F426981C66200D001FED46 /* Products */, 234 | 42F426CF1C6623C5001FED46 /* RxWebKit */, 235 | C4CB7D4422874E5600FB8D99 /* RxWebKitTests */, 236 | ); 237 | sourceTree = ""; 238 | }; 239 | 42F426981C66200D001FED46 /* Products */ = { 240 | isa = PBXGroup; 241 | children = ( 242 | 42F426971C66200D001FED46 /* Example.app */, 243 | 42F426CE1C6623C5001FED46 /* RxWebKit.framework */, 244 | C4CB7D4322874E5600FB8D99 /* RxWebKitTests-iOS.xctest */, 245 | EFC27F93246D7E9C0093D8B3 /* RxWebKitTests-macOS.xctest */, 246 | ); 247 | name = Products; 248 | sourceTree = ""; 249 | }; 250 | 42F426991C66200D001FED46 /* Example */ = { 251 | isa = PBXGroup; 252 | children = ( 253 | 42F426AF1C66223E001FED46 /* Base.lproj */, 254 | 42F426AD1C66223E001FED46 /* AppDelegate.swift */, 255 | 42F426AE1C66223E001FED46 /* Assets.xcassets */, 256 | 42F426AC1C66223E001FED46 /* Info.plist */, 257 | 42F426B41C66223E001FED46 /* ViewController.swift */, 258 | C4607FDA1FA0E68F002DA12F /* JavaScriptAlertPanelViewController.swift */, 259 | C4607FDC1FA0F1D3002DA12F /* JavaScriptConfirmPanelViewController.swift */, 260 | C4607FE01FA0F549002DA12F /* AuthChallengeViewController.swift */, 261 | C4878CD51FA1066C00B12C60 /* FailedRequestViewController.swift */, 262 | C4878CD71FA117E900B12C60 /* RedirectViewController.swift */, 263 | C4878CD91FA11B5A00B12C60 /* OtherRequestViewController.swift */, 264 | F68CEB9D22521B1D00498607 /* InvokeJSFunctionViewController.swift */, 265 | F68CEB9F22521B2B00498607 /* ObservingJSEventViewController.swift */, 266 | ); 267 | path = Example; 268 | sourceTree = ""; 269 | }; 270 | 42F426AF1C66223E001FED46 /* Base.lproj */ = { 271 | isa = PBXGroup; 272 | children = ( 273 | 42F426B01C66223E001FED46 /* LaunchScreen.storyboard */, 274 | 42F426B21C66223E001FED46 /* Main.storyboard */, 275 | ); 276 | path = Base.lproj; 277 | sourceTree = ""; 278 | }; 279 | 42F426CF1C6623C5001FED46 /* RxWebKit */ = { 280 | isa = PBXGroup; 281 | children = ( 282 | C4CA6B15228D752C0088A7A3 /* RxWebKit.podspec */, 283 | C4CA6B18228D75420088A7A3 /* Cartfile */, 284 | C4CA6B16228D75410088A7A3 /* Cartfile.private */, 285 | C4CA6B17228D75420088A7A3 /* Cartfile.resolved */, 286 | C4A50F9E229AF3EF0037E608 /* config.yml */, 287 | 42B12EFB1C6DFC7600C280BD /* Sources */, 288 | 42F426D21C6623C5001FED46 /* Info.plist */, 289 | 42F426D01C6623C5001FED46 /* RxWebKit.h */, 290 | ); 291 | path = RxWebKit; 292 | sourceTree = ""; 293 | }; 294 | 733B51F3D0F2B4B1E9A99FDC /* Frameworks */ = { 295 | isa = PBXGroup; 296 | children = ( 297 | C41360162287651B0061933C /* RxRelay.framework */, 298 | C4D323B3228748A0004B05A5 /* Nimble.framework */, 299 | C4D323B2228748A0004B05A5 /* Quick.framework */, 300 | C4D323B4228748A0004B05A5 /* RxTest.framework */, 301 | 424205831C89E9DF00214DA1 /* RxSwift.framework */, 302 | 424205841C89E9DF00214DA1 /* RxCocoa.framework */, 303 | ); 304 | name = Frameworks; 305 | sourceTree = ""; 306 | }; 307 | C4CB7D4422874E5600FB8D99 /* RxWebKitTests */ = { 308 | isa = PBXGroup; 309 | children = ( 310 | C4CB7D5322874EE700FB8D99 /* RxWebKitTests.swift */, 311 | C41360192288135F0061933C /* HasEventsBehavior.swift */, 312 | C4CA6B06228AB1680088A7A3 /* ForwardsEventsBehavior.swift */, 313 | C4CB7D4722874E5600FB8D99 /* Info.plist */, 314 | ); 315 | path = RxWebKitTests; 316 | sourceTree = ""; 317 | }; 318 | /* End PBXGroup section */ 319 | 320 | /* Begin PBXHeadersBuildPhase section */ 321 | 42F426CB1C6623C5001FED46 /* Headers */ = { 322 | isa = PBXHeadersBuildPhase; 323 | buildActionMask = 2147483647; 324 | files = ( 325 | 42F426D11C6623C5001FED46 /* RxWebKit.h in Headers */, 326 | ); 327 | runOnlyForDeploymentPostprocessing = 0; 328 | }; 329 | /* End PBXHeadersBuildPhase section */ 330 | 331 | /* Begin PBXNativeTarget section */ 332 | 42F426961C66200D001FED46 /* Example */ = { 333 | isa = PBXNativeTarget; 334 | buildConfigurationList = 42F426A91C66200D001FED46 /* Build configuration list for PBXNativeTarget "Example" */; 335 | buildPhases = ( 336 | 42F426931C66200D001FED46 /* Sources */, 337 | 42F426941C66200D001FED46 /* Frameworks */, 338 | 42F426951C66200D001FED46 /* Resources */, 339 | 42F426DA1C6623C5001FED46 /* Embed Frameworks */, 340 | ); 341 | buildRules = ( 342 | ); 343 | dependencies = ( 344 | 42F426D41C6623C5001FED46 /* PBXTargetDependency */, 345 | ); 346 | name = Example; 347 | productName = RxWebKit; 348 | productReference = 42F426971C66200D001FED46 /* Example.app */; 349 | productType = "com.apple.product-type.application"; 350 | }; 351 | 42F426CD1C6623C5001FED46 /* RxWebKit */ = { 352 | isa = PBXNativeTarget; 353 | buildConfigurationList = 42F426D71C6623C5001FED46 /* Build configuration list for PBXNativeTarget "RxWebKit" */; 354 | buildPhases = ( 355 | 42F426C91C6623C5001FED46 /* Sources */, 356 | 42F426CA1C6623C5001FED46 /* Frameworks */, 357 | 42F426CB1C6623C5001FED46 /* Headers */, 358 | 42F426CC1C6623C5001FED46 /* Resources */, 359 | ); 360 | buildRules = ( 361 | ); 362 | dependencies = ( 363 | ); 364 | name = RxWebKit; 365 | productName = RxWebKit; 366 | productReference = 42F426CE1C6623C5001FED46 /* RxWebKit.framework */; 367 | productType = "com.apple.product-type.framework"; 368 | }; 369 | C4CB7D4222874E5600FB8D99 /* RxWebKitTests-iOS */ = { 370 | isa = PBXNativeTarget; 371 | buildConfigurationList = C4CB7D4B22874E5600FB8D99 /* Build configuration list for PBXNativeTarget "RxWebKitTests-iOS" */; 372 | buildPhases = ( 373 | C4CB7D3F22874E5600FB8D99 /* Sources */, 374 | C4CB7D4022874E5600FB8D99 /* Frameworks */, 375 | C4CB7D4122874E5600FB8D99 /* Resources */, 376 | C41360102287644B0061933C /* Embed Frameworks */, 377 | ); 378 | buildRules = ( 379 | ); 380 | dependencies = ( 381 | C4CB7D4A22874E5600FB8D99 /* PBXTargetDependency */, 382 | ); 383 | name = "RxWebKitTests-iOS"; 384 | productName = RxWebKitTests; 385 | productReference = C4CB7D4322874E5600FB8D99 /* RxWebKitTests-iOS.xctest */; 386 | productType = "com.apple.product-type.bundle.unit-test"; 387 | }; 388 | EFC27F80246D7E9C0093D8B3 /* RxWebKitTests-macOS */ = { 389 | isa = PBXNativeTarget; 390 | buildConfigurationList = EFC27F90246D7E9C0093D8B3 /* Build configuration list for PBXNativeTarget "RxWebKitTests-macOS" */; 391 | buildPhases = ( 392 | EFC27F83246D7E9C0093D8B3 /* Sources */, 393 | EFC27F87246D7E9C0093D8B3 /* Frameworks */, 394 | EFC27F88246D7E9C0093D8B3 /* Resources */, 395 | EFC27F89246D7E9C0093D8B3 /* Embed Frameworks */, 396 | ); 397 | buildRules = ( 398 | ); 399 | dependencies = ( 400 | EFC27F81246D7E9C0093D8B3 /* PBXTargetDependency */, 401 | ); 402 | name = "RxWebKitTests-macOS"; 403 | productName = RxWebKitTests; 404 | productReference = EFC27F93246D7E9C0093D8B3 /* RxWebKitTests-macOS.xctest */; 405 | productType = "com.apple.product-type.bundle.unit-test"; 406 | }; 407 | /* End PBXNativeTarget section */ 408 | 409 | /* Begin PBXProject section */ 410 | 42F4268F1C66200D001FED46 /* Project object */ = { 411 | isa = PBXProject; 412 | attributes = { 413 | LastSwiftUpdateCheck = 1020; 414 | LastUpgradeCheck = 1020; 415 | ORGANIZATIONNAME = MokuMokuCloud; 416 | TargetAttributes = { 417 | 42F426961C66200D001FED46 = { 418 | CreatedOnToolsVersion = 7.2; 419 | DevelopmentTeamName = "Daichi Ichihara (Personal Team)"; 420 | LastSwiftMigration = 0800; 421 | }; 422 | 42F426CD1C6623C5001FED46 = { 423 | CreatedOnToolsVersion = 7.2; 424 | DevelopmentTeam = 6XJDQ2F5E3; 425 | DevelopmentTeamName = "Daichi Ichihara (Personal Team)"; 426 | LastSwiftMigration = 1020; 427 | }; 428 | C4CB7D4222874E5600FB8D99 = { 429 | CreatedOnToolsVersion = 10.2.1; 430 | ProvisioningStyle = Manual; 431 | }; 432 | EFC27F80246D7E9C0093D8B3 = { 433 | ProvisioningStyle = Manual; 434 | }; 435 | }; 436 | }; 437 | buildConfigurationList = 42F426921C66200D001FED46 /* Build configuration list for PBXProject "RxWebKit" */; 438 | compatibilityVersion = "Xcode 3.2"; 439 | developmentRegion = English; 440 | hasScannedForEncodings = 0; 441 | knownRegions = ( 442 | English, 443 | en, 444 | Base, 445 | ); 446 | mainGroup = 42F4268E1C66200D001FED46; 447 | productRefGroup = 42F426981C66200D001FED46 /* Products */; 448 | projectDirPath = ""; 449 | projectRoot = ""; 450 | targets = ( 451 | 42F426961C66200D001FED46 /* Example */, 452 | 42F426CD1C6623C5001FED46 /* RxWebKit */, 453 | C4CB7D4222874E5600FB8D99 /* RxWebKitTests-iOS */, 454 | EFC27F80246D7E9C0093D8B3 /* RxWebKitTests-macOS */, 455 | ); 456 | }; 457 | /* End PBXProject section */ 458 | 459 | /* Begin PBXResourcesBuildPhase section */ 460 | 42F426951C66200D001FED46 /* Resources */ = { 461 | isa = PBXResourcesBuildPhase; 462 | buildActionMask = 2147483647; 463 | files = ( 464 | 42F426B81C66223E001FED46 /* LaunchScreen.storyboard in Resources */, 465 | 42F426B71C66223E001FED46 /* Assets.xcassets in Resources */, 466 | 42F426B91C66223E001FED46 /* Main.storyboard in Resources */, 467 | ); 468 | runOnlyForDeploymentPostprocessing = 0; 469 | }; 470 | 42F426CC1C6623C5001FED46 /* Resources */ = { 471 | isa = PBXResourcesBuildPhase; 472 | buildActionMask = 2147483647; 473 | files = ( 474 | ); 475 | runOnlyForDeploymentPostprocessing = 0; 476 | }; 477 | C4CB7D4122874E5600FB8D99 /* Resources */ = { 478 | isa = PBXResourcesBuildPhase; 479 | buildActionMask = 2147483647; 480 | files = ( 481 | ); 482 | runOnlyForDeploymentPostprocessing = 0; 483 | }; 484 | EFC27F88246D7E9C0093D8B3 /* Resources */ = { 485 | isa = PBXResourcesBuildPhase; 486 | buildActionMask = 2147483647; 487 | files = ( 488 | ); 489 | runOnlyForDeploymentPostprocessing = 0; 490 | }; 491 | /* End PBXResourcesBuildPhase section */ 492 | 493 | /* Begin PBXSourcesBuildPhase section */ 494 | 42F426931C66200D001FED46 /* Sources */ = { 495 | isa = PBXSourcesBuildPhase; 496 | buildActionMask = 2147483647; 497 | files = ( 498 | C4607FE11FA0F549002DA12F /* AuthChallengeViewController.swift in Sources */, 499 | F68CEB9E22521B1D00498607 /* InvokeJSFunctionViewController.swift in Sources */, 500 | 42F426BA1C66223E001FED46 /* ViewController.swift in Sources */, 501 | C4878CDA1FA11B5A00B12C60 /* OtherRequestViewController.swift in Sources */, 502 | F68CEBA022521B2B00498607 /* ObservingJSEventViewController.swift in Sources */, 503 | C4878CD81FA117E900B12C60 /* RedirectViewController.swift in Sources */, 504 | C4607FDD1FA0F1D3002DA12F /* JavaScriptConfirmPanelViewController.swift in Sources */, 505 | C4878CD61FA1066C00B12C60 /* FailedRequestViewController.swift in Sources */, 506 | 42F426B61C66223E001FED46 /* AppDelegate.swift in Sources */, 507 | C4607FDB1FA0E68F002DA12F /* JavaScriptAlertPanelViewController.swift in Sources */, 508 | ); 509 | runOnlyForDeploymentPostprocessing = 0; 510 | }; 511 | 42F426C91C6623C5001FED46 /* Sources */ = { 512 | isa = PBXSourcesBuildPhase; 513 | buildActionMask = 2147483647; 514 | files = ( 515 | 42F426DC1C662CF9001FED46 /* Rx+WebKit.swift in Sources */, 516 | C48E61841F9E336000C6A753 /* WKNavigationDelegateEvents+Rx.swift in Sources */, 517 | C48E617E1F9E27A700C6A753 /* RxWKNavigationDelegateProxy.swift in Sources */, 518 | C4607FD01FA0DAC9002DA12F /* RxWKUIDelegateProxy.swift in Sources */, 519 | F6D7DD0D224F62860066C90F /* RxWKUserContentController.swift in Sources */, 520 | C4607FD61FA0DC40002DA12F /* RxWKUIDelegateEvents+Rx.swift in Sources */, 521 | ); 522 | runOnlyForDeploymentPostprocessing = 0; 523 | }; 524 | C4CB7D3F22874E5600FB8D99 /* Sources */ = { 525 | isa = PBXSourcesBuildPhase; 526 | buildActionMask = 2147483647; 527 | files = ( 528 | C4135FFE22875FBE0061933C /* RxWebKitTests.swift in Sources */, 529 | C4CA6B07228AB1680088A7A3 /* ForwardsEventsBehavior.swift in Sources */, 530 | C413601A2288135F0061933C /* HasEventsBehavior.swift in Sources */, 531 | ); 532 | runOnlyForDeploymentPostprocessing = 0; 533 | }; 534 | EFC27F83246D7E9C0093D8B3 /* Sources */ = { 535 | isa = PBXSourcesBuildPhase; 536 | buildActionMask = 2147483647; 537 | files = ( 538 | EFC27F84246D7E9C0093D8B3 /* RxWebKitTests.swift in Sources */, 539 | EFC27F85246D7E9C0093D8B3 /* ForwardsEventsBehavior.swift in Sources */, 540 | EFC27F86246D7E9C0093D8B3 /* HasEventsBehavior.swift in Sources */, 541 | ); 542 | runOnlyForDeploymentPostprocessing = 0; 543 | }; 544 | /* End PBXSourcesBuildPhase section */ 545 | 546 | /* Begin PBXTargetDependency section */ 547 | 42F426D41C6623C5001FED46 /* PBXTargetDependency */ = { 548 | isa = PBXTargetDependency; 549 | target = 42F426CD1C6623C5001FED46 /* RxWebKit */; 550 | targetProxy = 42F426D31C6623C5001FED46 /* PBXContainerItemProxy */; 551 | }; 552 | C4CB7D4A22874E5600FB8D99 /* PBXTargetDependency */ = { 553 | isa = PBXTargetDependency; 554 | target = 42F426CD1C6623C5001FED46 /* RxWebKit */; 555 | targetProxy = C4CB7D4922874E5600FB8D99 /* PBXContainerItemProxy */; 556 | }; 557 | EFC27F81246D7E9C0093D8B3 /* PBXTargetDependency */ = { 558 | isa = PBXTargetDependency; 559 | target = 42F426CD1C6623C5001FED46 /* RxWebKit */; 560 | targetProxy = EFC27F82246D7E9C0093D8B3 /* PBXContainerItemProxy */; 561 | }; 562 | /* End PBXTargetDependency section */ 563 | 564 | /* Begin PBXVariantGroup section */ 565 | 42F426B01C66223E001FED46 /* LaunchScreen.storyboard */ = { 566 | isa = PBXVariantGroup; 567 | children = ( 568 | 42F426B11C66223E001FED46 /* Base */, 569 | ); 570 | name = LaunchScreen.storyboard; 571 | path = .; 572 | sourceTree = ""; 573 | }; 574 | 42F426B21C66223E001FED46 /* Main.storyboard */ = { 575 | isa = PBXVariantGroup; 576 | children = ( 577 | 42F426B31C66223E001FED46 /* Base */, 578 | ); 579 | name = Main.storyboard; 580 | path = .; 581 | sourceTree = ""; 582 | }; 583 | /* End PBXVariantGroup section */ 584 | 585 | /* Begin XCBuildConfiguration section */ 586 | 42F426A71C66200D001FED46 /* Debug */ = { 587 | isa = XCBuildConfiguration; 588 | buildSettings = { 589 | ALWAYS_SEARCH_USER_PATHS = NO; 590 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 591 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 592 | CLANG_CXX_LIBRARY = "libc++"; 593 | CLANG_ENABLE_MODULES = YES; 594 | CLANG_ENABLE_OBJC_ARC = YES; 595 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 596 | CLANG_WARN_BOOL_CONVERSION = YES; 597 | CLANG_WARN_COMMA = YES; 598 | CLANG_WARN_CONSTANT_CONVERSION = YES; 599 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 600 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 601 | CLANG_WARN_EMPTY_BODY = YES; 602 | CLANG_WARN_ENUM_CONVERSION = YES; 603 | CLANG_WARN_INFINITE_RECURSION = YES; 604 | CLANG_WARN_INT_CONVERSION = YES; 605 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 606 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 607 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 608 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 609 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 610 | CLANG_WARN_STRICT_PROTOTYPES = YES; 611 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 612 | CLANG_WARN_UNREACHABLE_CODE = YES; 613 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 614 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 615 | COPY_PHASE_STRIP = NO; 616 | DEBUG_INFORMATION_FORMAT = dwarf; 617 | ENABLE_STRICT_OBJC_MSGSEND = YES; 618 | ENABLE_TESTABILITY = YES; 619 | "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]" = ( 620 | "$(SRCROOT)/Carthage/Build/iOS", 621 | "$(inherited)", 622 | ); 623 | "FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]" = ( 624 | "$(SRCROOT)/Carthage/Build/iOS", 625 | "$(inherited)", 626 | ); 627 | "FRAMEWORK_SEARCH_PATHS[sdk=macosx*]" = ( 628 | "$(SRCROOT)/Carthage/Build/Mac", 629 | "$(inherited)", 630 | ); 631 | GCC_C_LANGUAGE_STANDARD = gnu99; 632 | GCC_DYNAMIC_NO_PIC = NO; 633 | GCC_NO_COMMON_BLOCKS = YES; 634 | GCC_OPTIMIZATION_LEVEL = 0; 635 | GCC_PREPROCESSOR_DEFINITIONS = ( 636 | "DEBUG=1", 637 | "$(inherited)", 638 | ); 639 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 640 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 641 | GCC_WARN_UNDECLARED_SELECTOR = YES; 642 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 643 | GCC_WARN_UNUSED_FUNCTION = YES; 644 | GCC_WARN_UNUSED_VARIABLE = YES; 645 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 646 | MACOSX_DEPLOYMENT_TARGET = 10.13; 647 | MTL_ENABLE_DEBUG_INFO = YES; 648 | ONLY_ACTIVE_ARCH = YES; 649 | SDKROOT = ""; 650 | SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; 651 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 652 | SWIFT_VERSION = 5.0; 653 | }; 654 | name = Debug; 655 | }; 656 | 42F426A81C66200D001FED46 /* Release */ = { 657 | isa = XCBuildConfiguration; 658 | buildSettings = { 659 | ALWAYS_SEARCH_USER_PATHS = NO; 660 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 661 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 662 | CLANG_CXX_LIBRARY = "libc++"; 663 | CLANG_ENABLE_MODULES = YES; 664 | CLANG_ENABLE_OBJC_ARC = YES; 665 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 666 | CLANG_WARN_BOOL_CONVERSION = YES; 667 | CLANG_WARN_COMMA = YES; 668 | CLANG_WARN_CONSTANT_CONVERSION = YES; 669 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 670 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 671 | CLANG_WARN_EMPTY_BODY = YES; 672 | CLANG_WARN_ENUM_CONVERSION = YES; 673 | CLANG_WARN_INFINITE_RECURSION = YES; 674 | CLANG_WARN_INT_CONVERSION = YES; 675 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 676 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 677 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 678 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 679 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 680 | CLANG_WARN_STRICT_PROTOTYPES = YES; 681 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 682 | CLANG_WARN_UNREACHABLE_CODE = YES; 683 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 684 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 685 | COPY_PHASE_STRIP = NO; 686 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 687 | ENABLE_NS_ASSERTIONS = NO; 688 | ENABLE_STRICT_OBJC_MSGSEND = YES; 689 | "FRAMEWORK_SEARCH_PATHS[sdk=iphoneos*]" = ( 690 | "$(SRCROOT)/Carthage/Build/iOS", 691 | "$(inherited)", 692 | ); 693 | "FRAMEWORK_SEARCH_PATHS[sdk=iphonesimulator*]" = ( 694 | "$(SRCROOT)/Carthage/Build/iOS", 695 | "$(inherited)", 696 | ); 697 | "FRAMEWORK_SEARCH_PATHS[sdk=macosx*]" = ( 698 | "$(SRCROOT)/Carthage/Build/Mac", 699 | "$(inherited)", 700 | ); 701 | GCC_C_LANGUAGE_STANDARD = gnu99; 702 | GCC_NO_COMMON_BLOCKS = YES; 703 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 704 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 705 | GCC_WARN_UNDECLARED_SELECTOR = YES; 706 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 707 | GCC_WARN_UNUSED_FUNCTION = YES; 708 | GCC_WARN_UNUSED_VARIABLE = YES; 709 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 710 | MACOSX_DEPLOYMENT_TARGET = 10.13; 711 | MTL_ENABLE_DEBUG_INFO = NO; 712 | SDKROOT = ""; 713 | SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; 714 | SWIFT_VERSION = 5.0; 715 | VALIDATE_PRODUCT = YES; 716 | }; 717 | name = Release; 718 | }; 719 | 42F426AA1C66200D001FED46 /* Debug */ = { 720 | isa = XCBuildConfiguration; 721 | buildSettings = { 722 | CLANG_ENABLE_MODULES = YES; 723 | DEVELOPMENT_TEAM = ""; 724 | INFOPLIST_FILE = "$(SRCROOT)/Example/Info.plist"; 725 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 726 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 727 | PRODUCT_BUNDLE_IDENTIFIER = mokumoku.Example; 728 | PRODUCT_NAME = "$(TARGET_NAME)"; 729 | SDKROOT = iphoneos; 730 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 731 | SWIFT_VERSION = 5.0; 732 | }; 733 | name = Debug; 734 | }; 735 | 42F426AB1C66200D001FED46 /* Release */ = { 736 | isa = XCBuildConfiguration; 737 | buildSettings = { 738 | CLANG_ENABLE_MODULES = YES; 739 | DEVELOPMENT_TEAM = ""; 740 | INFOPLIST_FILE = "$(SRCROOT)/Example/Info.plist"; 741 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 742 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 743 | PRODUCT_BUNDLE_IDENTIFIER = mokumoku.Example; 744 | PRODUCT_NAME = "$(TARGET_NAME)"; 745 | SDKROOT = iphoneos; 746 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 747 | SWIFT_VERSION = 5.0; 748 | }; 749 | name = Release; 750 | }; 751 | 42F426D81C6623C5001FED46 /* Debug */ = { 752 | isa = XCBuildConfiguration; 753 | buildSettings = { 754 | CLANG_ENABLE_MODULES = YES; 755 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 756 | CURRENT_PROJECT_VERSION = 1; 757 | DEFINES_MODULE = YES; 758 | DYLIB_COMPATIBILITY_VERSION = 1; 759 | DYLIB_CURRENT_VERSION = 1; 760 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 761 | INFOPLIST_FILE = "$(SRCROOT)/RxWebKit/Info.plist"; 762 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 763 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 764 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 765 | PRODUCT_BUNDLE_IDENTIFIER = mokumoku.RxWebKit; 766 | PRODUCT_NAME = "$(TARGET_NAME)"; 767 | SKIP_INSTALL = YES; 768 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 769 | SWIFT_VERSION = 5.0; 770 | TARGETED_DEVICE_FAMILY = "1,2"; 771 | VERSIONING_SYSTEM = "apple-generic"; 772 | VERSION_INFO_PREFIX = ""; 773 | }; 774 | name = Debug; 775 | }; 776 | 42F426D91C6623C5001FED46 /* Release */ = { 777 | isa = XCBuildConfiguration; 778 | buildSettings = { 779 | CLANG_ENABLE_MODULES = YES; 780 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 781 | CURRENT_PROJECT_VERSION = 1; 782 | DEFINES_MODULE = YES; 783 | DYLIB_COMPATIBILITY_VERSION = 1; 784 | DYLIB_CURRENT_VERSION = 1; 785 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 786 | INFOPLIST_FILE = "$(SRCROOT)/RxWebKit/Info.plist"; 787 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 788 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 789 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 790 | PRODUCT_BUNDLE_IDENTIFIER = mokumoku.RxWebKit; 791 | PRODUCT_NAME = "$(TARGET_NAME)"; 792 | SKIP_INSTALL = YES; 793 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 794 | SWIFT_VERSION = 5.0; 795 | TARGETED_DEVICE_FAMILY = "1,2"; 796 | VERSIONING_SYSTEM = "apple-generic"; 797 | VERSION_INFO_PREFIX = ""; 798 | }; 799 | name = Release; 800 | }; 801 | C4CB7D4C22874E5600FB8D99 /* Debug */ = { 802 | isa = XCBuildConfiguration; 803 | buildSettings = { 804 | CLANG_ANALYZER_NONNULL = YES; 805 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 806 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 807 | CLANG_ENABLE_OBJC_WEAK = YES; 808 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 809 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 810 | CODE_SIGN_IDENTITY = "iPhone Developer"; 811 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; 812 | CODE_SIGN_STYLE = Manual; 813 | DEVELOPMENT_TEAM = ""; 814 | GCC_C_LANGUAGE_STANDARD = gnu11; 815 | INFOPLIST_FILE = RxWebKitTests/Info.plist; 816 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 817 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 818 | "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks @loader_path/../Frameworks"; 819 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 820 | MTL_FAST_MATH = YES; 821 | PRODUCT_BUNDLE_IDENTIFIER = bobgodwinx.RxWebKitTests; 822 | PRODUCT_NAME = "$(TARGET_NAME)"; 823 | PROVISIONING_PROFILE_SPECIFIER = ""; 824 | "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; 825 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 826 | SWIFT_VERSION = 5.0; 827 | TARGETED_DEVICE_FAMILY = "1,2"; 828 | }; 829 | name = Debug; 830 | }; 831 | C4CB7D4D22874E5600FB8D99 /* Release */ = { 832 | isa = XCBuildConfiguration; 833 | buildSettings = { 834 | CLANG_ANALYZER_NONNULL = YES; 835 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 836 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 837 | CLANG_ENABLE_OBJC_WEAK = YES; 838 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 839 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 840 | CODE_SIGN_IDENTITY = "iPhone Developer"; 841 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; 842 | CODE_SIGN_STYLE = Manual; 843 | DEVELOPMENT_TEAM = ""; 844 | GCC_C_LANGUAGE_STANDARD = gnu11; 845 | INFOPLIST_FILE = RxWebKitTests/Info.plist; 846 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 847 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 848 | "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks @loader_path/../Frameworks"; 849 | MTL_FAST_MATH = YES; 850 | PRODUCT_BUNDLE_IDENTIFIER = bobgodwinx.RxWebKitTests; 851 | PRODUCT_NAME = "$(TARGET_NAME)"; 852 | PROVISIONING_PROFILE_SPECIFIER = ""; 853 | "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; 854 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 855 | SWIFT_VERSION = 5.0; 856 | TARGETED_DEVICE_FAMILY = "1,2"; 857 | }; 858 | name = Release; 859 | }; 860 | EFC27F91246D7E9C0093D8B3 /* Debug */ = { 861 | isa = XCBuildConfiguration; 862 | buildSettings = { 863 | CLANG_ANALYZER_NONNULL = YES; 864 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 865 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 866 | CLANG_ENABLE_OBJC_WEAK = YES; 867 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 868 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 869 | CODE_SIGN_IDENTITY = "-"; 870 | CODE_SIGN_STYLE = Manual; 871 | DEVELOPMENT_TEAM = ""; 872 | GCC_C_LANGUAGE_STANDARD = gnu11; 873 | INFOPLIST_FILE = RxWebKitTests/Info.plist; 874 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 875 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 876 | "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks @loader_path/../Frameworks"; 877 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 878 | MTL_FAST_MATH = YES; 879 | PRODUCT_BUNDLE_IDENTIFIER = bobgodwinx.RxWebKitTests; 880 | PRODUCT_NAME = "$(TARGET_NAME)"; 881 | PROVISIONING_PROFILE_SPECIFIER = ""; 882 | SDKROOT = macosx; 883 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 884 | SWIFT_VERSION = 5.0; 885 | TARGETED_DEVICE_FAMILY = "1,2"; 886 | }; 887 | name = Debug; 888 | }; 889 | EFC27F92246D7E9C0093D8B3 /* Release */ = { 890 | isa = XCBuildConfiguration; 891 | buildSettings = { 892 | CLANG_ANALYZER_NONNULL = YES; 893 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 894 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 895 | CLANG_ENABLE_OBJC_WEAK = YES; 896 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 897 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 898 | CODE_SIGN_IDENTITY = "-"; 899 | CODE_SIGN_STYLE = Manual; 900 | DEVELOPMENT_TEAM = ""; 901 | GCC_C_LANGUAGE_STANDARD = gnu11; 902 | INFOPLIST_FILE = RxWebKitTests/Info.plist; 903 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 904 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 905 | "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks @loader_path/../Frameworks"; 906 | MTL_FAST_MATH = YES; 907 | PRODUCT_BUNDLE_IDENTIFIER = bobgodwinx.RxWebKitTests; 908 | PRODUCT_NAME = "$(TARGET_NAME)"; 909 | PROVISIONING_PROFILE_SPECIFIER = ""; 910 | SDKROOT = macosx; 911 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 912 | SWIFT_VERSION = 5.0; 913 | TARGETED_DEVICE_FAMILY = "1,2"; 914 | }; 915 | name = Release; 916 | }; 917 | /* End XCBuildConfiguration section */ 918 | 919 | /* Begin XCConfigurationList section */ 920 | 42F426921C66200D001FED46 /* Build configuration list for PBXProject "RxWebKit" */ = { 921 | isa = XCConfigurationList; 922 | buildConfigurations = ( 923 | 42F426A71C66200D001FED46 /* Debug */, 924 | 42F426A81C66200D001FED46 /* Release */, 925 | ); 926 | defaultConfigurationIsVisible = 0; 927 | defaultConfigurationName = Release; 928 | }; 929 | 42F426A91C66200D001FED46 /* Build configuration list for PBXNativeTarget "Example" */ = { 930 | isa = XCConfigurationList; 931 | buildConfigurations = ( 932 | 42F426AA1C66200D001FED46 /* Debug */, 933 | 42F426AB1C66200D001FED46 /* Release */, 934 | ); 935 | defaultConfigurationIsVisible = 0; 936 | defaultConfigurationName = Release; 937 | }; 938 | 42F426D71C6623C5001FED46 /* Build configuration list for PBXNativeTarget "RxWebKit" */ = { 939 | isa = XCConfigurationList; 940 | buildConfigurations = ( 941 | 42F426D81C6623C5001FED46 /* Debug */, 942 | 42F426D91C6623C5001FED46 /* Release */, 943 | ); 944 | defaultConfigurationIsVisible = 0; 945 | defaultConfigurationName = Release; 946 | }; 947 | C4CB7D4B22874E5600FB8D99 /* Build configuration list for PBXNativeTarget "RxWebKitTests-iOS" */ = { 948 | isa = XCConfigurationList; 949 | buildConfigurations = ( 950 | C4CB7D4C22874E5600FB8D99 /* Debug */, 951 | C4CB7D4D22874E5600FB8D99 /* Release */, 952 | ); 953 | defaultConfigurationIsVisible = 0; 954 | defaultConfigurationName = Release; 955 | }; 956 | EFC27F90246D7E9C0093D8B3 /* Build configuration list for PBXNativeTarget "RxWebKitTests-macOS" */ = { 957 | isa = XCConfigurationList; 958 | buildConfigurations = ( 959 | EFC27F91246D7E9C0093D8B3 /* Debug */, 960 | EFC27F92246D7E9C0093D8B3 /* Release */, 961 | ); 962 | defaultConfigurationIsVisible = 0; 963 | defaultConfigurationName = Release; 964 | }; 965 | /* End XCConfigurationList section */ 966 | }; 967 | rootObject = 42F4268F1C66200D001FED46 /* Project object */; 968 | } 969 | -------------------------------------------------------------------------------- /RxWebKit.xcodeproj/xcshareddata/xcschemes/Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /RxWebKit.xcodeproj/xcshareddata/xcschemes/RxWebKit.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 53 | 59 | 60 | 61 | 62 | 68 | 69 | 75 | 76 | 77 | 78 | 80 | 81 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /RxWebKit.xcodeproj/xcshareddata/xcschemes/RxWebKitTests-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 37 | 38 | 44 | 45 | 47 | 48 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /RxWebKit.xcodeproj/xcshareddata/xcschemes/RxWebKitTests-macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 37 | 38 | 44 | 45 | 47 | 48 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /RxWebKit.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RxWebKit.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /RxWebKitTests/ForwardsEventsBehavior.swift: -------------------------------------------------------------------------------- 1 | 2 | import Foundation 3 | import Quick 4 | import Nimble 5 | import RxSwift 6 | import RxCocoa 7 | import RxTest 8 | import WebKit 9 | @testable import RxWebKit 10 | 11 | struct ForwardsEventsBehaviorContext { 12 | let sut: WKWebView 13 | let scheduler: TestScheduler 14 | let selector: Selector 15 | let invoked: (() -> ()) 16 | 17 | init(_ sut: WKWebView, _ scheduler: TestScheduler, _ selector: Selector, invoked: @escaping(() -> ())) { 18 | self.sut = sut 19 | self.scheduler = scheduler 20 | self.selector = selector 21 | self.invoked = invoked 22 | } 23 | } 24 | 25 | class ForwardsEventsBehavior: Quick.Behavior { 26 | override class func spec(_ context: @escaping () -> ForwardsEventsBehaviorContext) { 27 | 28 | var sut: WKWebView! 29 | var scheduler: TestScheduler! 30 | var selector: Selector! 31 | var invoked: (() -> ())! 32 | 33 | beforeEach { 34 | let cxt = context() 35 | sut = cxt.sut 36 | scheduler = cxt.scheduler 37 | selector = cxt.selector 38 | invoked = cxt.invoked 39 | } 40 | 41 | afterEach { 42 | sut = nil 43 | scheduler = nil 44 | selector = nil 45 | } 46 | 47 | describe("Has Events Behavior") { 48 | it("sentMessage") { 49 | SharingScheduler.mock(scheduler: scheduler) { 50 | let sentMessage = scheduler.record(source: sut.rx.delegate.sentMessage(selector)) 51 | invoked() 52 | scheduler.start() 53 | expect(sentMessage.events.count).to(equal(1)) 54 | } 55 | } 56 | 57 | it("methodInvoke") { 58 | SharingScheduler.mock(scheduler: scheduler) { 59 | let methodInvoked = scheduler.record(source: sut.rx.delegate.methodInvoked(selector)) 60 | invoked() 61 | scheduler.start() 62 | expect(methodInvoked.events.count).to(equal(1)) 63 | } 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /RxWebKitTests/HasEventsBehavior.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Quick 3 | import Nimble 4 | import RxSwift 5 | import RxCocoa 6 | import RxTest 7 | import WebKit 8 | @testable import RxWebKit 9 | 10 | struct HasEventsBehaviorContext { 11 | let scheduler: TestScheduler 12 | let observable: Observable 13 | let expected: T? 14 | init(_ scheduler: TestScheduler, _ observable: Observable, _ expected: T?) { 15 | self.scheduler = scheduler 16 | self.observable = observable 17 | self.expected = expected 18 | } 19 | } 20 | 21 | class HasEventsBehavior: Quick.Behavior> { 22 | override class func spec(_ context: @escaping () -> HasEventsBehaviorContext) { 23 | var scheduler: TestScheduler! 24 | var observable: Observable! 25 | var expected: T? 26 | 27 | beforeEach { 28 | let cxt = context() 29 | scheduler = cxt.scheduler 30 | observable = cxt.observable 31 | expected = cxt.expected 32 | } 33 | 34 | afterEach { 35 | scheduler = nil 36 | observable = nil 37 | } 38 | 39 | describe("Has Events Behavior") { 40 | it("Actually got the event") { 41 | SharingScheduler.mock(scheduler: scheduler) { 42 | let recorded = scheduler.record(source: observable) 43 | scheduler.start() 44 | expect(recorded.events.count).to(equal(1)) 45 | expect(recorded.events[0].value.element).to(equal(expected)) 46 | } 47 | } 48 | } 49 | } 50 | } 51 | 52 | extension TestScheduler { 53 | /// Builds testable observer for s specific observable sequence, binds it's results and sets up disposal. 54 | /// parameter source: Observable sequence to observe. 55 | /// returns: Observer that records all events for observable sequence. 56 | func record(source: O) -> TestableObserver { 57 | let observer = self.createObserver(O.Element.self) 58 | let disposable = source.asObservable().bind(to: observer) 59 | self.scheduleAt(100000) { 60 | disposable.dispose() 61 | } 62 | return observer 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /RxWebKitTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /RxWebKitTests/RxWebKitTests.swift: -------------------------------------------------------------------------------- 1 | import WebKit 2 | import Quick 3 | import Nimble 4 | import RxSwift 5 | import RxTest 6 | @testable import RxWebKit 7 | 8 | class RxWebKitTests: QuickSpec { 9 | override func spec() { 10 | var scheduler: TestScheduler! 11 | var sut: WKWebView! 12 | let html = """ 13 | 14 | 15 | 16 | RxWebKit 17 | 18 | 19 | 20 |

This is a Heading

21 |

This is a paragraph.

22 | 23 | 24 | 25 | """ 26 | 27 | beforeEach { 28 | scheduler = TestScheduler(initialClock: 0) 29 | sut = WKWebView(frame: CGRect.zero) 30 | } 31 | 32 | afterEach { 33 | scheduler = nil 34 | sut = nil 35 | } 36 | 37 | itBehavesLike(HasEventsBehavior.self) { 38 | HasEventsBehaviorContext(scheduler, sut.rx.title, "") 39 | } 40 | 41 | itBehavesLike(HasEventsBehavior.self) { 42 | sut.loadHTMLString(html, baseURL: Bundle.main.resourceURL) 43 | return HasEventsBehaviorContext(scheduler, sut.rx.loading, true) 44 | } 45 | 46 | itBehavesLike(HasEventsBehavior.self) { 47 | HasEventsBehaviorContext(scheduler, sut.rx.loading, false) 48 | } 49 | 50 | itBehavesLike(HasEventsBehavior.self) { 51 | HasEventsBehaviorContext(scheduler, sut.rx.estimatedProgress, 0.0) 52 | } 53 | 54 | itBehavesLike(HasEventsBehavior.self) { 55 | sut.loadHTMLString(html, baseURL: Bundle.main.resourceURL) 56 | return HasEventsBehaviorContext(scheduler, sut.rx.estimatedProgress, 0.1) 57 | } 58 | 59 | itBehavesLike(HasEventsBehavior.self) { 60 | HasEventsBehaviorContext(scheduler, sut.rx.canGoBack, false) 61 | } 62 | 63 | itBehavesLike(HasEventsBehavior.self) { 64 | HasEventsBehaviorContext(scheduler, sut.rx.canGoForward, false) 65 | } 66 | 67 | itBehavesLike(ForwardsEventsBehavior.self) { 68 | ForwardsEventsBehaviorContext(sut, scheduler, .didTerminate) { 69 | sut.navigationDelegate?.webViewWebContentProcessDidTerminate?(sut) 70 | } 71 | } 72 | 73 | itBehavesLike(ForwardsEventsBehavior.self) { 74 | ForwardsEventsBehaviorContext(sut, scheduler, .didCommitNavigation) { 75 | let navigation = sut.loadHTMLString(html, baseURL: Bundle.main.resourceURL) 76 | sut.navigationDelegate?.webView?(sut, didCommit: navigation) 77 | } 78 | } 79 | 80 | itBehavesLike(ForwardsEventsBehavior.self) { 81 | ForwardsEventsBehaviorContext(sut, scheduler, .didFinishNavigation) { 82 | let navigation = sut.loadHTMLString(html, baseURL: Bundle.main.resourceURL) 83 | sut.navigationDelegate?.webView?(sut, didFinish: navigation) 84 | } 85 | } 86 | 87 | itBehavesLike(ForwardsEventsBehavior.self) { 88 | ForwardsEventsBehaviorContext(sut, scheduler, .didReceiveServerRedirectForProvisionalNavigation) { 89 | let navigation = sut.loadHTMLString(html, baseURL: Bundle.main.resourceURL) 90 | sut.navigationDelegate?.webView?(sut, didReceiveServerRedirectForProvisionalNavigation: navigation) 91 | } 92 | } 93 | 94 | itBehavesLike(ForwardsEventsBehavior.self) { 95 | ForwardsEventsBehaviorContext(sut, scheduler, .didFailNavigation) { 96 | let navigation = sut.loadHTMLString(html, baseURL: Bundle.main.resourceURL) 97 | sut.navigationDelegate?.webView?(sut, didFail: navigation, withError: TestError.didFailNavigation) 98 | } 99 | } 100 | 101 | itBehavesLike(ForwardsEventsBehavior.self) { 102 | ForwardsEventsBehaviorContext(sut, scheduler, .didFailProvisionalNavigation) { 103 | let navigation = sut.loadHTMLString(html, baseURL: Bundle.main.resourceURL) 104 | sut.navigationDelegate?.webView?(sut, didFailProvisionalNavigation: navigation, withError: TestError.didFailProvisionalNavigation) 105 | } 106 | } 107 | 108 | itBehavesLike(ForwardsEventsBehavior.self) { 109 | ForwardsEventsBehaviorContext(sut, scheduler, .didReceiveChallenge) { 110 | let protectionSpace = URLProtectionSpace(host: "fake", port: 8443, protocol: nil, realm: nil, authenticationMethod: nil) 111 | let credential = URLCredential(user: "bad-user", password: "bad-password", persistence: URLCredential.Persistence.forSession) 112 | let sender = MockURLAuthenticationChallengeSender() 113 | 114 | let challenge = URLAuthenticationChallenge(protectionSpace: protectionSpace, proposedCredential: credential, previousFailureCount: 0, failureResponse:nil, error: TestError.didReceiveChallenge, sender: sender) 115 | 116 | sut.navigationDelegate?.webView?(sut, didReceive: challenge, completionHandler: { (_, _) in }) 117 | } 118 | } 119 | 120 | itBehavesLike(ForwardsEventsBehavior.self) { 121 | ForwardsEventsBehaviorContext(sut, scheduler, .decidePolicyNavigationAction) { 122 | let delegate:WKNavigationDelegate? = sut.navigationDelegate 123 | delegate?.webView?(sut, decidePolicyFor: WKNavigationAction(), decisionHandler: { (_) in }) 124 | } 125 | } 126 | 127 | itBehavesLike(ForwardsEventsBehavior.self) { 128 | ForwardsEventsBehaviorContext(sut, scheduler, .decidePolicyNavigationResponse) { 129 | let delegate:WKNavigationDelegate? = sut.navigationDelegate 130 | delegate?.webView?(sut, decidePolicyFor: WKNavigationResponse(), decisionHandler: { (_) in }) 131 | } 132 | } 133 | } 134 | } 135 | 136 | enum TestError: Error { 137 | case didFailNavigation 138 | case didFailProvisionalNavigation 139 | case didReceiveChallenge 140 | } 141 | 142 | @objc class MockURLAuthenticationChallengeSender: NSObject, URLAuthenticationChallengeSender { 143 | override func `self`() -> Self { 144 | return self 145 | } 146 | 147 | override init() {} 148 | func use(_ credential: URLCredential, for challenge: URLAuthenticationChallenge) { } 149 | 150 | func continueWithoutCredential(for challenge: URLAuthenticationChallenge) {} 151 | 152 | func cancel(_ challenge: URLAuthenticationChallenge) { } 153 | } 154 | -------------------------------------------------------------------------------- /Sources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.3 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Sources/RxWebKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // RxWebKit.h 3 | // RxWebKit 4 | // 5 | // Created by Daichi Ichihara on 2016/02/06. 6 | // Copyright © 2016年 RxSwift Community. All rights reserved. 7 | // 8 | 9 | #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR 10 | #import 11 | #elseif TARGET_OS_MAC 12 | #endif 13 | //! Project version number for RxWebKit. 14 | extern double RxWebKitVersionNumber; 15 | 16 | //! Project version string for RxWebKit. 17 | extern const unsigned char RxWebKitVersionString[]; 18 | 19 | // In this header, you should import all the public headers of your framework using statements like #import 20 | 21 | 22 | -------------------------------------------------------------------------------- /Sources/RxWebKit/Rx+WebKit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Rx+WebKit.swift 3 | // RxWebKit 4 | // 5 | // Created by Daichi Ichihara on 2016/02/06. 6 | // Copyright © 2016年 RxSwift Community. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import WebKit 11 | import RxSwift 12 | import RxCocoa 13 | 14 | extension Reactive where Base: WKWebView { 15 | /** 16 | Reactive wrapper for `title` property 17 | */ 18 | public var title: Observable { 19 | return self.observeWeakly(String.self, "title") 20 | } 21 | 22 | /** 23 | Reactive wrapper for `loading` property. 24 | */ 25 | public var loading: Observable { 26 | return self.observeWeakly(Bool.self, "loading") 27 | .map { $0 ?? false } 28 | } 29 | 30 | /** 31 | Reactive wrapper for `estimatedProgress` property. 32 | */ 33 | public var estimatedProgress: Observable { 34 | return self.observeWeakly(Double.self, "estimatedProgress") 35 | .map { $0 ?? 0.0 } 36 | } 37 | 38 | /** 39 | Reactive wrapper for `url` property. 40 | */ 41 | public var url: Observable { 42 | return self.observeWeakly(URL.self, "URL") 43 | } 44 | 45 | 46 | /** 47 | Reactive wrapper for `canGoBack` property. 48 | */ 49 | public var canGoBack: Observable { 50 | return self.observeWeakly(Bool.self, "canGoBack") 51 | .map { $0 ?? false } 52 | } 53 | 54 | /** 55 | Reactive wrapper for `canGoForward` property. 56 | */ 57 | public var canGoForward: Observable { 58 | return self.observeWeakly(Bool.self, "canGoForward") 59 | .map { $0 ?? false } 60 | } 61 | 62 | 63 | /// Reactive wrapper for `evaluateJavaScript(_:completionHandler:)` method. 64 | /// 65 | /// - Parameter javaScriptString: The JavaScript string to evaluate. 66 | /// - Returns: Observable sequence of result of the script evaluation. 67 | public func evaluateJavaScript(_ javaScriptString:String) -> Observable { 68 | return Observable.create { [weak base] observer in 69 | base?.evaluateJavaScript(javaScriptString) { value, error in 70 | if let error = error { 71 | observer.onError(error) 72 | } else { 73 | observer.onNext(value) 74 | observer.onCompleted() 75 | } 76 | } 77 | return Disposables.create() 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Sources/RxWebKit/RxWKNavigationDelegateProxy.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxWKNavigationDelegateProxy.swift 3 | // RxWebKit 4 | // 5 | // Created by Bob Obi on 23.10.17. 6 | // Copyright © 2017 RxSwift Community. All rights reserved. 7 | // 8 | 9 | import WebKit 10 | #if !RX_NO_MODULE 11 | import RxSwift 12 | import RxCocoa 13 | #endif 14 | 15 | public typealias RxWKNavigationDelegate = DelegateProxy 16 | 17 | open class RxWKNavigationDelegateProxy: RxWKNavigationDelegate, DelegateProxyType, WKNavigationDelegate { 18 | 19 | /// Type of parent object 20 | public weak private(set) var webView: WKWebView? 21 | 22 | /// Init with ParentObject 23 | public init(parentObject: ParentObject) { 24 | webView = parentObject 25 | super.init(parentObject: parentObject, delegateProxy: RxWKNavigationDelegateProxy.self) 26 | } 27 | 28 | /// Register self to known implementations 29 | public static func registerKnownImplementations() { 30 | self.register { parent -> RxWKNavigationDelegateProxy in 31 | RxWKNavigationDelegateProxy(parentObject: parent) 32 | } 33 | } 34 | 35 | /// Gets the current `WKNavigationDelegate` on `WKWebView` 36 | open class func currentDelegate(for object: ParentObject) -> WKNavigationDelegate? { 37 | return object.navigationDelegate 38 | } 39 | 40 | /// Set the navigationDelegate for `WKWebView` 41 | open class func setCurrentDelegate(_ delegate: WKNavigationDelegate?, to object: ParentObject) { 42 | object.navigationDelegate = delegate 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/RxWebKit/RxWKUIDelegateEvents+Rx.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxWKUIDelegateEvents+Rx.swift 3 | // RxWebKit 4 | // 5 | // Created by Bob Obi on 25.10.17. 6 | // Copyright © 2017 RxSwift Community. All rights reserved. 7 | // 8 | 9 | #if !RX_NO_MODULE 10 | import RxSwift 11 | import RxCocoa 12 | #endif 13 | 14 | import WebKit 15 | 16 | extension Reactive where Base: WKWebView { 17 | public typealias JSAlertEvent = (webView: WKWebView, message: String, frame: WKFrameInfo, handler: () -> ()) 18 | public typealias JSConfirmEvent = (webView: WKWebView, message: String, frame: WKFrameInfo, handler: (Bool) -> ()) 19 | #if os(iOS) 20 | public typealias CommitPreviewEvent = (webView: WKWebView, controller: UIViewController) 21 | #endif 22 | 23 | /// Reactive wrapper for `navigationDelegate`. 24 | public var uiDelegate: DelegateProxy { 25 | return RxWKUIDelegateProxy.proxy(for: base) 26 | } 27 | 28 | /// Reactive wrapper for `func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Swift.Void)` 29 | public var javaScriptAlertPanel: ControlEvent { 30 | typealias __CompletionHandler = @convention(block) () -> () 31 | let source:Observable = uiDelegate 32 | .methodInvoked(.jsAlert).map { args in 33 | let view = try castOrThrow(WKWebView.self, args[0]) 34 | let message = try castOrThrow(String.self, args[1]) 35 | let frame = try castOrThrow(WKFrameInfo.self, args[2]) 36 | var closureObject: AnyObject? = nil 37 | var mutableArgs = args 38 | mutableArgs.withUnsafeMutableBufferPointer { ptr in 39 | closureObject = ptr[3] as AnyObject 40 | } 41 | let __completionBlockPtr = UnsafeRawPointer(Unmanaged.passUnretained(closureObject as AnyObject).toOpaque()) 42 | let handler = unsafeBitCast(__completionBlockPtr, to: __CompletionHandler.self) 43 | return (view, message, frame, handler) 44 | } 45 | 46 | return ControlEvent(events: source) 47 | } 48 | 49 | /// Reactive wrapper for `func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Swift.Void)` 50 | public var javaScriptConfirmPanel: ControlEvent { 51 | typealias __ConfirmHandler = @convention(block) (Bool) -> () 52 | let source:Observable = uiDelegate 53 | .methodInvoked(.jsConfirm).map { args in 54 | let view = try castOrThrow(WKWebView.self, args[0]) 55 | let message = try castOrThrow(String.self, args[1]) 56 | let frame = try castOrThrow(WKFrameInfo.self, args[2]) 57 | var closureObject: AnyObject? = nil 58 | var mutableArgs = args 59 | mutableArgs.withUnsafeMutableBufferPointer { ptr in 60 | closureObject = ptr[3] as AnyObject 61 | } 62 | let __confirmBlockPtr = UnsafeRawPointer(Unmanaged.passUnretained(closureObject as AnyObject).toOpaque()) 63 | let handler = unsafeBitCast(__confirmBlockPtr, to: __ConfirmHandler.self) 64 | return (view, message, frame, handler) 65 | } 66 | 67 | return ControlEvent(events: source) 68 | } 69 | 70 | #if os(iOS) 71 | /// Reactive wrappper for `func webView(_ webView: WKWebView, commitPreviewingViewController previewingViewController: UIViewController)` 72 | @available(iOS 10.0, *) 73 | public var commitPreviewing: ControlEvent { 74 | let source: Observable = uiDelegate 75 | .methodInvoked(.commitPreviewing) 76 | .map { args in 77 | let view = try castOrThrow(WKWebView.self, args[0]) 78 | let controller = try castOrThrow(UIViewController.self, args[1]) 79 | return (view, controller) 80 | } 81 | 82 | return ControlEvent(events: source) 83 | } 84 | #endif 85 | } 86 | 87 | fileprivate extension Selector { 88 | static let jsAlert = #selector(WKUIDelegate.webView(_:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:)) 89 | static let jsConfirm = #selector(WKUIDelegate.webView(_:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler:)) 90 | 91 | #if os(iOS) 92 | @available(iOS 10.0, *) 93 | static let commitPreviewing = #selector(WKUIDelegate.webView(_:commitPreviewingViewController:)) 94 | #endif 95 | } 96 | -------------------------------------------------------------------------------- /Sources/RxWebKit/RxWKUIDelegateProxy.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxWKUIDelegateProxy.swift 3 | // RxWebKit 4 | // 5 | // Created by Bob Obi on 25.10.17. 6 | // Copyright © 2017 RxSwift Community. All rights reserved. 7 | // 8 | 9 | import WebKit 10 | #if !RX_NO_MODULE 11 | import RxSwift 12 | import RxCocoa 13 | #endif 14 | 15 | public typealias RxWKUIDelegate = DelegateProxy 16 | 17 | open class RxWKUIDelegateProxy: RxWKUIDelegate, DelegateProxyType, WKUIDelegate { 18 | 19 | /// Type of parent object 20 | /// must be WKWebView! 21 | public weak private(set) var webView: WKWebView? 22 | 23 | /// Init with ParentObject 24 | public init(parentObject: ParentObject) { 25 | webView = parentObject 26 | super.init(parentObject: parentObject, delegateProxy: RxWKUIDelegateProxy.self) 27 | } 28 | 29 | /// Register self to known implementations 30 | public static func registerKnownImplementations() { 31 | self.register { parent -> RxWKUIDelegateProxy in 32 | RxWKUIDelegateProxy(parentObject: parent) 33 | } 34 | } 35 | 36 | /// Gets the current `WKUIDelegate` on `WKWebView` 37 | open class func currentDelegate(for object: ParentObject) -> WKUIDelegate? { 38 | return object.uiDelegate 39 | } 40 | 41 | /// Set the uiDelegate for `WKWebView` 42 | open class func setCurrentDelegate(_ delegate: WKUIDelegate?, to object: ParentObject) { 43 | object.uiDelegate = delegate 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/RxWebKit/RxWKUserContentController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxWKUserContentController.swift 3 | // RxWebKit 4 | // 5 | // Created by Jesse Hao on 2019/3/30. 6 | // Copyright © 2019 RxSwift Community. All rights reserved. 7 | // 8 | 9 | import WebKit 10 | #if !RX_NO_MODULE 11 | import RxSwift 12 | import RxCocoa 13 | #endif 14 | 15 | extension WKUserContentController { 16 | fileprivate class MessageHandler : NSObject, WKScriptMessageHandler { 17 | typealias MessageReceiveHandler = (WKScriptMessage) -> Void 18 | private var messageReceiveHandler:MessageReceiveHandler? 19 | 20 | func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { 21 | self.messageReceiveHandler?(message) 22 | } 23 | 24 | func onReceive(_ handler:@escaping MessageReceiveHandler) { 25 | self.messageReceiveHandler = handler 26 | } 27 | } 28 | } 29 | 30 | public extension Reactive where Base : WKUserContentController { 31 | /// Observable sequence of script message. 32 | /// 33 | /// - Parameter name: The name of the message handler 34 | /// - Returns: Observable sequence of script message. 35 | func scriptMessage(forName name:String) -> ControlEvent { 36 | return ControlEvent(events: Observable.create { [weak base] observer in 37 | let handler = WKUserContentController.MessageHandler() 38 | base?.add(handler, name: name) 39 | handler.onReceive { 40 | observer.onNext($0) 41 | } 42 | return Disposables.create { 43 | base?.removeScriptMessageHandler(forName: name) 44 | } 45 | }) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Sources/RxWebKit/WKNavigationDelegateEvents+Rx.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WKNavigationDelegateEvents+Rx.swift 3 | // RxWebKit 4 | // 5 | // Created by Bob Obi on 23.10.17. 6 | // Copyright © 2017 RxSwift Community. All rights reserved. 7 | // 8 | 9 | #if !RX_NO_MODULE 10 | import RxSwift 11 | import RxCocoa 12 | #endif 13 | 14 | import WebKit 15 | 16 | func castOrThrow(_ resultType: T.Type, _ object: Any) throws -> T { 17 | guard let returnValue = object as? T else { 18 | throw RxCocoaError.castingError(object: object, targetType: resultType) 19 | } 20 | return returnValue 21 | } 22 | 23 | extension Reactive where Base: WKWebView { 24 | /// WKNavigationEvent emits a tuple that contains both 25 | /// WKWebView + WKNavigation 26 | public typealias WKNavigationEvent = (webView: WKWebView, navigation: WKNavigation) 27 | 28 | /// WKNavigationFailedEvent emits a tuple that contains both 29 | /// WKWebView + WKNavigation + Swift.Error 30 | public typealias WKNavigationFailEvent = (webView: WKWebView, navigation: WKNavigation, error: Error) 31 | 32 | /// ChallengeHandler this is exposed to the user on subscription 33 | public typealias ChallengeHandler = (URLSession.AuthChallengeDisposition, URLCredential?) -> Void 34 | /// WKNavigationChallengeEvent emits a tuple event of WKWebView + challenge + ChallengeHandler 35 | public typealias WKNavigationChallengeEvent = (webView: WKWebView, challenge: URLAuthenticationChallenge, handler: ChallengeHandler) 36 | 37 | /// DecisionHandler this is the block exposed to the user on subscription 38 | public typealias DecisionHandler = (WKNavigationResponsePolicy) -> Void 39 | /// WKNavigationResponsePolicyEvent emits a tuple event of WKWebView + WKNavigationResponse + DecisionHandler 40 | public typealias WKNavigationResponsePolicyEvent = ( webView: WKWebView, reponse: WKNavigationResponse, handler: DecisionHandler) 41 | /// ActionHandler this is the block exposed to the user on subscription 42 | public typealias ActionHandler = (WKNavigationActionPolicy) -> Void 43 | /// WKNavigationActionPolicyEvent emits a tuple event of WKWebView + WKNavigationAction + ActionHandler 44 | public typealias WKNavigationActionPolicyEvent = ( webView: WKWebView, action: WKNavigationAction, handler: ActionHandler) 45 | 46 | /// Reactive wrapper for `navigationDelegate`. 47 | public var delegate: DelegateProxy { 48 | return RxWKNavigationDelegateProxy.proxy(for: base) 49 | } 50 | 51 | /// Reactive wrapper for delegate method `webView(_ webView: WKWebView, didCommit navigation: WKNavigation!)`. 52 | public var didCommitNavigation: ControlEvent { 53 | let source: Observable = delegate 54 | .methodInvoked(.didCommitNavigation) 55 | .map { arg in 56 | let view = try castOrThrow(WKWebView.self, arg[0]) 57 | let nav = try castOrThrow(WKNavigation.self, arg[1]) 58 | return (view, nav) 59 | } 60 | return ControlEvent(events: source) 61 | } 62 | 63 | /// Reactive wrapper for delegate method `webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!)`. 64 | public var didStartProvisionalNavigation: ControlEvent { 65 | let source: Observable = delegate 66 | .methodInvoked(.didStartProvisionalNavigation) 67 | .map { arg in 68 | let view = try castOrThrow(WKWebView.self, arg[0]) 69 | let nav = try castOrThrow(WKNavigation.self, arg[1]) 70 | return (view, nav) 71 | } 72 | return ControlEvent(events: source) 73 | } 74 | 75 | /// Reactive wrapper for delegate method `webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)` 76 | public var didFinishNavigation: ControlEvent { 77 | let source: Observable = delegate 78 | .methodInvoked(.didFinishNavigation) 79 | .map { arg in 80 | let view = try castOrThrow(WKWebView.self, arg[0]) 81 | let nav = try castOrThrow(WKNavigation.self, arg[1]) 82 | return (view, nav) 83 | } 84 | return ControlEvent(events: source) 85 | } 86 | 87 | /// Reactive wrapper for delegate method `webViewWebContentProcessDidTerminate(_ webView: WKWebView)`. 88 | @available(iOS 9.0, *) 89 | public var didTerminate: ControlEvent { 90 | let source: Observable = delegate 91 | .methodInvoked(.didTerminate) 92 | .map { try castOrThrow(WKWebView.self, $0[0]) } 93 | return ControlEvent(events: source) 94 | } 95 | 96 | /// Reactive wrapper for delegate method `webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!)`. 97 | public var didReceiveServerRedirectForProvisionalNavigation: ControlEvent { 98 | let source: Observable = delegate 99 | .methodInvoked(.didReceiveServerRedirectForProvisionalNavigation) 100 | .map { arg in 101 | let view = try castOrThrow(WKWebView.self, arg[0]) 102 | let nav = try castOrThrow(WKNavigation.self, arg[1]) 103 | return (view, nav) 104 | } 105 | return ControlEvent(events: source) 106 | } 107 | 108 | /// Reactive wrapper for delegate method `webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error)`. 109 | public var didFailNavigation: ControlEvent { 110 | let source: Observable = delegate 111 | .methodInvoked(.didFailNavigation) 112 | .map { arg in 113 | let view = try castOrThrow(WKWebView.self, arg[0]) 114 | let nav = try castOrThrow(WKNavigation.self, arg[1]) 115 | let error = try castOrThrow(Swift.Error.self, arg[2]) 116 | return (view, nav, error) 117 | } 118 | return ControlEvent(events: source) 119 | } 120 | 121 | /// Reactive wrapper for delegate method `webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error)`. 122 | public var didFailProvisionalNavigation: ControlEvent { 123 | let source: Observable = delegate 124 | .methodInvoked(.didFailProvisionalNavigation) 125 | .map { arg in 126 | let view = try castOrThrow(WKWebView.self, arg[0]) 127 | let nav = try castOrThrow(WKNavigation.self, arg[1]) 128 | let error = try castOrThrow(Swift.Error.self, arg[2]) 129 | return (view, nav, error) 130 | } 131 | return ControlEvent(events: source) 132 | } 133 | 134 | /// Reactive wrapper for delegate method `webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)` 135 | public var didReceiveChallenge: ControlEvent { 136 | /// __ChallengeHandler is same as ChallengeHandler 137 | /// They are interchangeable, __ChallengeHandler is for internal use. 138 | /// ChallengeHandler is exposed to the user on subscription. 139 | /// @convention attribute makes the swift closure compatible with Objc blocks 140 | typealias __ChallengeHandler = @convention(block) (URLSession.AuthChallengeDisposition, URLCredential?) -> Void 141 | /*! @abstract Invoked when the web view needs to respond to an authentication challenge. 142 | @param webView The web view that received the authentication challenge. 143 | @param challenge The authentication challenge. 144 | @param completionHandler The completion handler you must invoke to respond to the challenge. The 145 | disposition argument is one of the constants of the enumerated type 146 | NSURLSessionAuthChallengeDisposition. When disposition is NSURLSessionAuthChallengeUseCredential, 147 | the credential argument is the credential to use, or nil to indicate continuing without a 148 | credential. 149 | @discussion If you do not implement this method, the web view will respond to the authentication challenge with the NSURLSessionAuthChallengeRejectProtectionSpace disposition. 150 | */ 151 | let source: Observable = delegate 152 | .sentMessage(.didReceiveChallenge) 153 | .map { arg in 154 | /// Extracting the WKWebView from the array at index zero 155 | /// which is the first argument of the function signature 156 | let view = try castOrThrow(WKWebView.self, arg[0]) 157 | /// Extracting the URLAuthenticationChallenge from the array at index one 158 | /// which is the second argument of the function signature 159 | let challenge = try castOrThrow(URLAuthenticationChallenge.self, arg[1]) 160 | /// Now you `Can't` transform closure easily because they are excuted 161 | /// in the stack if try it you will get the famous error 162 | /// `Could not cast value of type '__NSStackBlock__' (0x12327d1a8) to` 163 | /// this is because closures are transformed into a system type which is `__NSStackBlock__` 164 | /// the above mentioned type is not exposed to `developer`. So everytime 165 | /// you execute a closure the compiler transforms it into this Object. 166 | /// So you go through the following steps to get a human readable type 167 | /// of the closure signature: 168 | /// 1. closureObject is type of AnyObject to that holds the raw value from 169 | /// the array. 170 | var closureObject: AnyObject? = nil 171 | /// 2. make the array mutable in order to access the `withUnsafeMutableBufferPointer` 172 | /// fuctionalities 173 | var mutableArg = arg 174 | /// 3. Grab the closure at index 3 of the array, but we have to use the C-style 175 | /// approach to access the raw memory underpinning the array and store it in closureObject 176 | /// Now the object stored in the `closureObject` is `Unmanaged` and `some unspecified type` 177 | /// the intelligent swift compiler doesn't know what sort of type it contains. It is Raw. 178 | mutableArg.withUnsafeMutableBufferPointer { ptr in 179 | closureObject = ptr[2] as AnyObject 180 | } 181 | /// 4. instantiate an opaque pointer to referenc the value of the `unspecified type` 182 | let __challengeBlockPtr = UnsafeRawPointer(Unmanaged.passUnretained(closureObject as AnyObject).toOpaque()) 183 | /// 5. Here the magic happen we forcefully tell the compiler that anything 184 | /// found at this memory address that is refrenced should be a type of 185 | /// `__ChallengeHandler`! 186 | let handler = unsafeBitCast(__challengeBlockPtr, to: __ChallengeHandler.self) 187 | return (view, challenge, handler) 188 | } 189 | 190 | return ControlEvent(events: source) 191 | 192 | /** 193 | Reference: 194 | 195 | This is a holy grail part for more information please read the following articles. 196 | 1: http://codejaxy.com/q/332345/ios-objective-c-memory-management-automatic-ref-counting-objective-c-blocks-understand-one-edge-case-of-block-memory-management-in-objc 197 | 2: http://www.galloway.me.uk/2012/10/a-look-inside-blocks-episode-2/ 198 | 3: https://maniacdev.com/2013/11/tutorial-an-in-depth-guide-to-objective-c-block-debugging 199 | 4: get know how [__NSStackBlock__ + UnsafeRawPointer + unsafeBitCast] works under the hood 200 | 5: https://en.wikipedia.org/wiki/Opaque_pointer 201 | 6: https://stackoverflow.com/questions/43662363/cast-objective-c-block-nsstackblock-into-swift-3 202 | */ 203 | } 204 | 205 | /// Reactive wrapper for `func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Swift.Void)` 206 | public var decidePolicyNavigationResponse: ControlEvent { 207 | typealias __DecisionHandler = @convention(block) (WKNavigationResponsePolicy) -> () 208 | let source:Observable = delegate 209 | .methodInvoked(.decidePolicyNavigationResponse).map { args in 210 | let view = try castOrThrow(WKWebView.self, args[0]) 211 | let response = try castOrThrow(WKNavigationResponse.self, args[1]) 212 | var closureObject: AnyObject? = nil 213 | var mutableArgs = args 214 | mutableArgs.withUnsafeMutableBufferPointer { ptr in 215 | closureObject = ptr[2] as AnyObject 216 | } 217 | let __decisionBlockPtr = UnsafeRawPointer(Unmanaged.passUnretained(closureObject as AnyObject).toOpaque()) 218 | let handler = unsafeBitCast(__decisionBlockPtr, to: __DecisionHandler.self) 219 | return (view, response, handler) 220 | } 221 | 222 | return ControlEvent(events: source) 223 | } 224 | 225 | /// Reactive wrapper for `func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Swift.Void)` 226 | public var decidePolicyNavigationAction: ControlEvent { 227 | typealias __ActionHandler = @convention(block) (WKNavigationActionPolicy) -> () 228 | let source:Observable = delegate 229 | .methodInvoked(.decidePolicyNavigationAction).map { args in 230 | let view = try castOrThrow(WKWebView.self, args[0]) 231 | let action = try castOrThrow(WKNavigationAction.self, args[1]) 232 | var closureObject: AnyObject? = nil 233 | var mutableArgs = args 234 | mutableArgs.withUnsafeMutableBufferPointer { ptr in 235 | closureObject = ptr[2] as AnyObject 236 | } 237 | let __actionBlockPtr = UnsafeRawPointer(Unmanaged.passUnretained(closureObject as AnyObject).toOpaque()) 238 | let handler = unsafeBitCast(__actionBlockPtr, to: __ActionHandler.self) 239 | return (view, action, handler) 240 | } 241 | 242 | return ControlEvent(events: source) 243 | } 244 | } 245 | extension Selector { 246 | static let didCommitNavigation = #selector(WKNavigationDelegate.webView(_:didCommit:)) 247 | static let didStartProvisionalNavigation = #selector(WKNavigationDelegate.webView(_:didStartProvisionalNavigation:)) 248 | static let didFinishNavigation = #selector(WKNavigationDelegate.webView(_:didFinish:)) 249 | static let didReceiveServerRedirectForProvisionalNavigation = #selector(WKNavigationDelegate.webView(_:didReceiveServerRedirectForProvisionalNavigation:)) 250 | static let didFailNavigation = #selector(WKNavigationDelegate.webView(_:didFail:withError:)) 251 | static let didFailProvisionalNavigation = #selector(WKNavigationDelegate.webView(_:didFailProvisionalNavigation:withError:)) 252 | static let didReceiveChallenge = #selector(WKNavigationDelegate.webView(_:didReceive:completionHandler:)) 253 | @available(iOS 9.0, *) 254 | static let didTerminate = #selector(WKNavigationDelegate.webViewWebContentProcessDidTerminate(_:)) 255 | /// Xcode give error when selectors results into having same signature 256 | /// because of swift style you get for example: 257 | /// Ambiguous use of 'webView(_:decidePolicyFor:decisionHandler:)' 258 | /// please see this link for further understanding 259 | /// https://bugs.swift.org/browse/SR-3062 260 | #if swift(>=5.7) 261 | static let decidePolicyNavigationResponse = #selector(WKNavigationDelegate.webView(_:decidePolicyFor:decisionHandler:) as (WKNavigationDelegate) -> ((WKWebView, WKNavigationResponse, @escaping(WKNavigationResponsePolicy) -> Void) -> Void)?) 262 | static let decidePolicyNavigationAction = #selector(WKNavigationDelegate.webView(_:decidePolicyFor:decisionHandler:) as (WKNavigationDelegate) -> ((WKWebView, WKNavigationAction, @escaping (WKNavigationActionPolicy) -> Void) -> Void)?) 263 | #else 264 | static let decidePolicyNavigationResponse = #selector(WKNavigationDelegate.webView(_:decidePolicyFor:decisionHandler:) as ((WKNavigationDelegate) -> (WKWebView, WKNavigationResponse, @escaping(WKNavigationResponsePolicy) -> Void) -> Void)?) 265 | static let decidePolicyNavigationAction = #selector(WKNavigationDelegate.webView(_:decidePolicyFor:decisionHandler:) as ((WKNavigationDelegate) -> (WKWebView, WKNavigationAction, @escaping(WKNavigationActionPolicy) -> Void) -> Void)?) 266 | #endif 267 | } 268 | 269 | 270 | --------------------------------------------------------------------------------