WKCookieWebView.swift 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. //
  2. // WKCookieWebView.swift
  3. //
  4. import Foundation
  5. import WebKit
  6. class WKCookieWebView : WKWebView {
  7. private let useRedirectCookieHandling: Bool
  8. init(frame: CGRect, configuration: WKWebViewConfiguration, useRedirectCookieHandling: Bool = false) {
  9. self.useRedirectCookieHandling = useRedirectCookieHandling
  10. super.init(frame: frame, configuration: configuration)
  11. }
  12. required init?(coder: NSCoder) {
  13. self.useRedirectCookieHandling = false
  14. super.init(coder: coder)
  15. }
  16. override func load(_ request: URLRequest) -> WKNavigation? {
  17. var request = request
  18. let language = NSLocale.preferredLanguages[0] as String
  19. request.setValue(CCUtility.getUserAgent(), forHTTPHeaderField: "User-Agent")
  20. request.addValue("true", forHTTPHeaderField: "OCS-APIRequest")
  21. request.addValue(language, forHTTPHeaderField: "Accept-Language")
  22. guard useRedirectCookieHandling else {
  23. return super.load(request)
  24. }
  25. requestWithCookieHandling(request, success: { (newRequest , response, data) in
  26. DispatchQueue.main.async {
  27. self.syncCookiesInJS()
  28. if let data = data, let response = response {
  29. let _ = self.webViewLoad(data: data, response: response)
  30. }
  31. }
  32. }, failure: {
  33. // let WKWebView handle the network error
  34. DispatchQueue.main.async {
  35. let _ = super.load(request)
  36. }
  37. })
  38. return nil
  39. }
  40. private func requestWithCookieHandling(_ request: URLRequest, success: @escaping (URLRequest, HTTPURLResponse?, Data?) -> Void, failure: @escaping () -> Void) {
  41. let sessionConfig = URLSessionConfiguration.default
  42. let session = URLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil)
  43. let task = session.dataTask(with: request) { (data, response, error) in
  44. if let _ = error {
  45. failure()
  46. } else {
  47. if let response = response as? HTTPURLResponse {
  48. success(request, response, data)
  49. }
  50. }
  51. }
  52. task.resume()
  53. }
  54. private func webViewLoad(data: Data, response: URLResponse) -> WKNavigation! {
  55. guard let url = response.url else {
  56. return nil
  57. }
  58. let encode = response.textEncodingName ?? "utf8"
  59. let mine = response.mimeType ?? "text/html"
  60. return self.load(data, mimeType: mine, characterEncodingName: encode, baseURL: url)
  61. }
  62. }
  63. extension WKCookieWebView {
  64. // MARK: - JS Cookie handling
  65. private func syncCookiesInJS(for request: URLRequest? = nil) {
  66. if let url = request?.url,
  67. let cookies = HTTPCookieStorage.shared.cookies(for: url) {
  68. let script = jsCookiesString(for: cookies)
  69. let cookieScript = WKUserScript(source: script, injectionTime: .atDocumentStart, forMainFrameOnly: false)
  70. self.configuration.userContentController.addUserScript(cookieScript)
  71. } else if let cookies = HTTPCookieStorage.shared.cookies {
  72. let script = jsCookiesString(for: cookies)
  73. let cookieScript = WKUserScript(source: script, injectionTime: .atDocumentStart, forMainFrameOnly: false)
  74. self.configuration.userContentController.addUserScript(cookieScript)
  75. }
  76. }
  77. private func jsCookiesString(for cookies: [HTTPCookie]) -> String {
  78. var result = ""
  79. let dateFormatter = DateFormatter()
  80. dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
  81. dateFormatter.dateFormat = "EEE, d MMM yyyy HH:mm:ss zzz"
  82. for cookie in cookies {
  83. result += "document.cookie='\(cookie.name)=\(cookie.value); domain=\(cookie.domain); path=\(cookie.path); "
  84. if let date = cookie.expiresDate {
  85. result += "expires=\(dateFormatter.string(from: date)); "
  86. }
  87. if (cookie.isSecure) {
  88. result += "secure; "
  89. }
  90. result += "'; "
  91. }
  92. return result
  93. }
  94. }
  95. extension WKCookieWebView : URLSessionTaskDelegate {
  96. func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) {
  97. completionHandler(request)
  98. }
  99. }